当前位置: 首页 > 后端技术 > Java

Spring-BeanDefinition简析

时间:2023-04-02 00:01:46 Java

前言BeanDefinition是Spring中一个非常重要的类。在学习Springboot和Spring的时候,经常会遇到这个类,所以本文将介绍BeanDefinition的概念,一起学习。定义BeanDefinitionRegistry接口,用于注册、移除和查询BeanDefinition。本文用于帮助理解BeanDefinition的概念,同时作为作者学习Springboot的笔记,如有概念错误请批评指正。Springbootversion:2.4.1正文一、BeanDefinition简析BeanDefinition是Spring中的一个重要接口。BeanDefinition的实现类用于描述一个bean在Spring中应该被实例化的各种属性,包括bean的属性值和构造函数。、方法等信息,此外还额外描述了bean在Spring容器中的作用域、bean名称等信息。BeanDefinition可以类比为Java中类的Class对象。在Java中,可以使用类的Class对象来完成对象的实例化,但是在Spring中,并不能简单地使用bean的Class对象来完成bean的实例化,因为Spring中的bean有一些附加的属性,比如bean是否是单例,bean在容器中是否懒加载,bean在容器中的名称。这些属性不能用类的Class对象来描述,所以Spring引入了BeanDefinition来描述Spring应该实例化的bean的各种属性之一。Spring框架启动时,会在bean工厂后处理器ConfigurationClassPostProcessor中扫描需要加载到容器中的bean,并创建一个BeanDefinition,然后缓存在BeanFactory的beanDefinitionMap中。beanDefinitionMap是一个用来存储BeanDefinition的Map。key是容器中bean的名称,value是bean对应的BeanDefinition。下面以Springboot启动为例,简单展示一个流程,ConfigurationClassPostProcessor扫描需要加载到容器中的bean,创建一个BeanDefinition,然后放到beanDefinitionMap中。Springboot启动时,会先创建容器(也叫应用上下文),然后调用SpringApplication的refreshContext()方法初始化容器。在refreshContext()方法中,最终会调用容器的refresh()方法完成初始化。这个调用链可以表示如下。在AbstractApplicationContext的refresh()方法中,会调用invokeBeanFactoryPostProcessors()方法调用bean工厂后处理器,这里会调用ConfigurationClassPostProcessor。ConfigurationClassPostProcessor的具体逻辑这里暂且不分析,现在断点在AbstractApplicationContext#refresh()方法中调用invokeBeanFactoryPostProcessors()方法的代码行,此时,观察beanDefinitionMap在BeanFactory中如下。预先定义了一个bean,如下图。@ComponentpublicclassTestBean{publicTestBean(){System.out.println("初始化TestBean。");}}此时往回执行一步,然后观察BeanFactory中的beanDefinitionMap如下图。可以看到BeanFactory中的beanDefinitionMap中多了很多BeanDefinitions,包括预定义的TestBean。这是因为在ConfigurationClassPostProcessor中,所有需要加载到容器中的bean都会被扫描并创建到BeanDefinition中,然后存储到beanDefinitionMap中,但是TestBean的构造函数中应该打印的信息并没有打印出来,也就是说ConfigurationClassPostProcessor中只会创建BeanDefinition,并存储在beanDefinitionMap中,并不会真正实例化bean。真正的bean是通过实例化的finishBeanFactoryInitialization的AbstractApplicationContext()方法启用的,这里不再分析。现在可以看出Spring是使用BeanDefinition在容器中创建bean的。容器中的每个bean都将由一个BeanDefinition来描述。描述包括bean属性值、构造函数、方法、bean作用域和bean名称等信息。当Spring启动时,会先扫描所有需要加载到容器中的bean,然后为这些bean创建BeanDefinitions,并添加到BeanFactory中的beanDefinitionMap中。创建BeanDefinition时不实例化bean,创建BeanDefinition后才实例化bean。2、BeanDefinitionRegistry简析Springboot启动时会创建一个容器,根据WebApplicationType创建不同的容器。例如WebApplicationType为SERVLET,此时使用的容器为AnnotationConfigServletWebServerApplicationContext。如果WebApplicationType为NONE,此时使用的容器为AnnotationConfigApplicationContext。无论使用哪个容器,其内部都持有一个BeanFactory容器,其实际类型为DefaultListableBeanFactory。DefaultListableBeanFactory是一个带有注册功能的容器,因为它实现了BeanDefinitionRegistry接口。BeanDefinitionRegistry接口定义了BeanDefinition的注册、移除、查询等操作。下面主要看DefaultListableBeanFactory注册BeanDefinition的实现,即registerBeanDefinition()方法。源代码如下。@OverridepublicvoidregisterBeanDefinition(StringbeanName,BeanDefinitionbeanDefinition)throwsBeanDefinitionStoreException{Assert.hasText(beanName,"Bean名称不能为空");Assert.notNull(beanDefinition,"BeanDefinition不能为空");if(beanDefinitioninstanceofAbstractBean){try{((AbstractBeanDefinition)beanDefinition).validate();}catch(BeanDefinitionValidationExceptionex){thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(),beanName,“bean定义验证失败”,ex);}}//通过beanName获取注册的BeanDefinitionBeanDefinitionexistingDefinition=this.beanDefinitionMap.get(beanName);if(existingDefinition!=null){//如果BeanDefinition已经用当前的beanName注册过//判断是否允许注册不同的beanName同一个beanNameBeanDefinition覆盖已有的BeanDefinitionif(!isAllowBeanDefinitionOverriding()){thrownewBeanDefinitionOverrideException(beanName,beanDefinition,existingDefinition);}elseif(existingDefinition.getRole()updatedDefinitions=newArrayList<>(this.beanDefinitionNames.size()+1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(bean姓名);this.beanDefinitionNames=updatedDefinitions;//从manualSingletonNames中移除beanName以防止beanName重复//manualSingletonNames缓存手动注册的单例的名称removeManualSingletonName(beanName);}}else{//还在启动注册阶段this.beanDefinitionMap.put(beanName,beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames=null;}if(existingDefinition!=null||containsSingleton(beanName)){resetBeanDefinition(beanName);}elseif(isConfigurationFrozen()){clearByTypeCache();}}在DefaultListableBeanFactory实现的registerBeanDefinition()方法中,beanName和BeanDefinition会以键值对的形式缓存在beanDefinitionMap中,beanName也会被添加到beanDefinitionNames中由于仍然存在手动注册单例bean的情况,如果手动注册单例bean,单例bean的名称会缓存在manualSingletonNames中,所以还需要保证beanDefinitionNames和manualSingletonNames中的beanName不重复在registerBeanDefinition()方法中。现在做一个部分。Springboot启动时,创建的容器会持有一个DefaultListableBeanFactory容器,该容器实现了BeanDefinitionRegistry接口,具有注册、删除、查询BeanDefinition的功能。Springboot会扫描并加载bean,并自动组装它们,会根据需要加载到容器中的bean创建一个BeanDefinition,并将创建的BeanDefinition注册到DefaultListableBeanFactory容器中。总结一下,BeanDefinition的主要作用是描述Spring中的bean,可以类比Java中的Class对象,但是比Class对象能描述更多的bean信息,比如bean的作用域,是否懒加载等等。在Springboot的启动阶段,每一个需要加载到容器中的bean都会被创建为一个BeanDefinition然后注册到容器中,而Springboot中使用的容器持有一个DefaultListableBeanFactory,它实现了BeanDefinitionRegistry接口,所以将BeanDefinition注册到容器中其实就是将BeanDefinition注册到DefaultListableBeanFactory中,最后每一个BeanDefinition都会缓存到DefaultListableBeanFactory的beanDefinitionMap中。BeanDefinition只是一个接口,它有很多实现类,本文不做分析。同时在创建BeanDefinition的过程中还有一个重要的类ConfigurationClassPostProcessor。本文不深究。以上问题将在后续文章中一一描述。BeanDefinition只是一个接口,它有很多实现类,本文不做分析。同时在创建BeanDefinition的过程中还有一个重要的类ConfigurationClassPostProcessor。本文不深究。以上问题将在后续文章中一一描述。

最新推荐
猜你喜欢