本文基于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
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