Spring Bean注解方式加载BeanDefinition并实例化全过程

  本文以利用Dubbo框架开发rpc服务端为例详细阐述注解方式配置类的解析、数据保存、实例化以及注入到容器中。

  通常,涉及到配置参数的框架类,基本都离不开配置的解析及配置的保存;至于配置的具体使用各框架就自行处理了。对于Spring container而言,配置参数的具体使用就是在实例化Bean的时候使用(可以参考Spring的核心思想之IOC:仿Spring自定义一个实现IOC的容器)。本文主要分两大部分:1、Spring配置类的解析及数据保存;2、Spring Bean的实例化并注入。

  具体示例配置如下:

            

             

  跟踪调试Spring的工作流程:

           

  查看这个类的属性:

            

  各属性的属性如下:

      

   构造函数首先调用父类GenericApplicationContext构造函数初始化BeanFactory,这个类中的属性beanDefinitionMap保存bean初始化需要的相关信息。

              

  其次本类中初始化reader和scanner, 分别用于记录BeanDefinition和扫描可能的Bean。初始化之后reader和scanner后,其内部属性如下:

              

  一、配置类的解析及数据保存(register)

 

     通过源码跟踪分析,此过程的最终目的就是将通用的注解配置以<key,value>的形式保存在DefaultListableBeanFactory的beanDefinitionMap中,使所有的bean全部暴露以便后续使用。该暴露非依赖循环,仅最外层可识别的所有bean(可参考Spring的核心思想之IOC:仿Spring自定义一个实现IOC的容器)。

  二、Bean的实例化并注入(refresh)

    beanDefinitionMap中有很多的bean定义,本文仅关注自定义的配置类providerConfiguration的实例化注入。在模板方法设计模式的文章(五、模板方法设计模式及在Spring中的应用)中,以该方法阐述了实例化注册的大致执行流程。

    refresh方法中并没有传入beanFactory的参数,那么就得在此方法中获得beanFactory,具体子流程如下:

                  

    进一步跟踪调试,prepareBeanFactory方法中对beanFactory相关属性进行框架本身的初始赋值(即与具体业务无关)。其中关于beanPostProcessor的如下:

            

             

             

    进入方法invokeBeanFactoryPostProcessors(beanFactory)在context中调用Bean工厂后置处理器依赖处理beanDefinitionMap中的bean定义,将其依赖的bean注入到beanDefinitionMap中。核心方法为processConfigBeanDefinitions(registry),通过该方法即可知其为处理配置bean定义。其处理核心逻辑为:

    1、将所有的配置类列出

            

     2、配置解析对象ConfigurationClassParser解析候选配置类保存至ConfigurationClass中:按层次递归处理配置类及其子类

            

       与示例配置类相关的解析注解有@PropertySource、@Import(@EnableDubbo中包含)、@Bean。属性相关的解析不做详细说明,主要阐述@Import/@Bean的解析。

    3、@Import与@Bean的处理

    在@Enable*注解的工作原理 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中,粗略涉及了@EnableDubbo。这里仅此分析@EnableDubbo中涉及动态注册Bean(导入与ImportBeanDefinitionRegistrar相关)处理。在解析器中,通过configClass.addImportBeanDefinitionRegistrar直接将ImportBeanDefinitionRegistrar作为配置类保存至ConfigClass中。

            

      对于dubbo自定义的注解也是@Import动态注册Bean的组合注解:

            

               

      同理,@Bean标记的Bean方法应该保存在beanMethods中。至此,与业务逻辑相关的bean定义全部保存到了configClasses中。最后通过loadBeanDefinitionsForConfigurationClass全部保存至beanDefinitionMap中。具体时序图如下:              

          

      接下来的处理才是真正的实例化处理:

     4、bean实例化

      非懒加载的实例化均在refresh#finishBeanFactoryInitialization中,跟踪调试大致流程如下:

   

       如果有代理实例化前利用beanPostProcessor将原有bean替换为目标对象返回,没有则直接利用反射实例化对象。关键部分如下:

          

posted on 2021-06-25 09:03  池塘里洗澡的鸭子  阅读(872)  评论(0编辑  收藏  举报