Spring boot自动装配过程源码

本文基于org.springframework.boot 2.2.13.RELEASE版,详细说明了spring boot项目中META-INF/spring.factories文件内EnableAutoConfiguration下的类是如何自动装配的。

更具体的说,一般介绍spring boot自动装配原理的文章都会提到AutoConfigurationImportSelector里返回的EnableAutoConfiguration实现了自动装配,但没有回答是在spring boot启动的哪一步调用的。本文尝试从spring boot的run方法开始,逐步梳理到最终返回EnableAutoConfiguration,完成自动装配。

目录

1. 注册自动装配相关的bean工厂增强器:ConfigurationClassPostProcessor

2. 以主方法类为BD源,装载BD

3. 调用bean工厂增强器ConfigurationClassPostProcessor

4. ConfigurationClassPostProcessor主要处理逻辑

5. ConfigurationClassParser parse方法的主要处理逻辑

6. doProcessConfigurationClass方法与@Import处理

7. deferredImportSelectorHandler.process()方法

8. AutoConfigurationImportSelector


1. 注册自动装配相关的bean工厂增强器:ConfigurationClassPostProcessor

入口在方法org.springframework.boot.SpringApplication.run(java.lang.String...)中创建应用程序上下文一步:

一般web应用是SERVLET类型,应用程序上下文类型为AnnotationConfigServletWebServerApplicationContext

在调用BeanUtils实例化过程中,执行AnnotationConfigServletWebServerApplicationContext的构造方法。在构造方法中实例化了一个AnnotatedBeanDefinitionReader对象

实例化BD (bean definition) reader的过程最终会调用到AnnotationConfigUtils.registerAnnotationConfigProcessors(BeanDefinitionRegistry)方法

该方法会注册所有注解配置相关的处理程序

在该方法中,如果beanFactory中没有名为org.springframework.context.annotation.internalConfigurationAnnotationProcessor的BD,

就会注册一个,实际类型为ConfigurationClassPostProcessor。

2. 以主方法类为BD源,装载BD

spring boot项目在启动时一般代码如下,主方法所在的类一般会有注解@SpringBootApplication,并且作为run方法的第一个入参。

此入参经过传递最终存储在实例化后的SpringApplication的实例变量primarySources中

在SpringApplication.run方法准备上下文一步中,getAllSources会取出primarySources中的源,并由load方法加载到bean工厂中。

load方法最终会调用到

org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean,

将DemoApplication类包装成AnnotatedGenericBeanDefinition

并存入Bean工厂:

3. 调用bean工厂增强器ConfigurationClassPostProcessor

这一步spring boot的入口任然在SpringApplication.run方法中,是refreshContext方法,底层调用的就是spring的核心方法AbstractApplicationContext.refresh。

而bean工厂增强器ConfigurationClassPostProcessor的调用自然也在AbstractApplicationContext.refresh方法中的invokeBeanFactoryPostProcessors一步

在调用bean工厂增强器的过程中,会从bean工厂中取出BeanDefinitionRegistryPostProcessor类型的bean,取出的bean名字为org.springframework.context.annotation.internalConfigurationAnnotationProcessor,就是第一步加载的ConfigurationClassPostProcessor。

获取到ConfigurationClassPostProcessor后,最终调用其postProcessBeanDefinitionRegistry方法。

4. ConfigurationClassPostProcessor主要处理逻辑

首先选出要处理的BD。方法入参registry类型是DefaultListableBeanFactory,首先从Bean工厂中取出所有BD作为备选,通过方法ConfigurationClassUtils.checkConfigurationClassCandidate筛选出需要处理的BD,可以看到最终选出一个,正是第二步中添加的主方法所在的类

之后会通过ConfigurationClassParser类来解析demoApplication BD,经过parse, validate两步,

最终getConfigurationClasses方法返回通过解析demoApplication 获得的BD,

并且通过ConfigurationClassBeanDefinitionReader读入bean工厂

ConfigurationClassParser类的注释

5. ConfigurationClassParser parse方法的主要处理逻辑

parse方法如下,首先会根据bd的类型执行选择具体的parse方法,最终会执行org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass。在这个过程中遇到了实现延迟导入选择器DeferredImportSelector接口的类,会判断是否要延迟处理,需要延迟处理则先记录下来,由parse方法最下面的deferredImportSelectorHandler.process()处理。

EnableAutoConfiguration注解中import的AutoConfigurationImportSelector,就是在上述parse步骤中存入deferredImportSelectorHandler,并再最后deferredImportSelectorHandler.process()处理的。自动装备也就是在这个处理过程中实现的。

6. doProcessConfigurationClass方法与@Import处理

doProcessConfigurationClass方法主题框架如下,可以看到该方法对配置类的每个部分都做了相应的处理。对于自动装配,所关心的就是处理@Import注解的过程。

    /**
     * Apply processing and build a complete {@link ConfigurationClass} by reading the
     * annotations, members and methods from the source class. This method can be called
     * multiple times as relevant sources are discovered.
     * @param configClass the configuration class being build
     * @param sourceClass a source class
     * @return the superclass, or {@code null} if none found or previously processed
     */
    @Nullable
    protected final SourceClass doProcessConfigurationClass(
            ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
            throws IOException {

        // Process any @Component annotations
        ...

        // Process any @PropertySource annotations
        ...

        // Process any @ComponentScan annotations
        ...

        // Process any @Import annotations
        processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

        // Process any @ImportResource annotations
        ...

        // Process individual @Bean methods
        ...

        // Process default methods on interfaces
        ...

        // Process superclass, if any
        ...

        // No superclass -> processing is complete
        return null;
    }

首先getImports方法将注解@SpringBootApplication继承的两个@Import中指定的类获取到,其中一个就是上面说的AutoConfigurationImportSelector

在processImports方法中,由于AutoConfigurationImportSelector实现了DeferredImportSelector接口,并判定为需要延迟导入,因此暂时不处理,等到第5步中的deferredImportSelectorHandler.process()再处理

7. deferredImportSelectorHandler.process()方法

process中实例化了一个名为handler的DeferredImportSelectorGroupingHandler对象,并将deferredImports中的对象逐个注册到handler中,

最后调用handler.processGroupImports处理。从debug信息可以看到,注册到handler中的就是AutoConfigurationImportSelector。

进入processGroupImports,此时经过handler.register方法的注册,AutoConfigurationImportSelector已经包含在了grouping中。

调用grouping.getImports()方法,返回结果就是AutoConfigurationImportSelector加载的所有自动装配类的全限定名。

这些加载到的自动装备类在下面的processImports中被逐个处理。

现在主要关注getImports方法是如何加载自动装备类的。

getImports方法返回entry的个数取决于项目引入的依赖,例如当项目依赖org.springframework.boot:spring-boot-starter-data-jpa时,返回中就会包含jpa相关的类名。另外这里的group类,实际就是AutoConfigurationImportSelector中的一个内部类

8. AutoConfigurationImportSelector

从第7步最后getImports方法中可以看到,首先调用的是group.process方法,该方法通过调用AutoConfigurationImportSelector.getAutoConfigurationEntry方法获取了所有自动装配的类。

在getAutoConfigurationEntry方法中,首先调用了getCandidateConfigurations方法,从该方法中可以看出实际返回了124个可供自动装配的候选类。之后通过去重、排除、过滤等步骤,最终返回的就是上面的23个类。

开始返回的124个自动装配候选类就是spring-boot-autoconfigure包中META-INF/spring.factories文件EnableAutoConfiguration下的124个记录,开头截图如下。最后一条记录在第145行,总共124个。

在getCandidateConfigurations方法中,通过getSpringFactoriesLoaderFactoryClass方法返回了需要用SpringFactoriesLoader载入的类型,就是EnableAutoConfiguration

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值