自动加载BeanFactoryPostProcessor
在SpringBoot下,我们并没有告诉Spring应该扫描那个xml配置,也没有告诉Spring是否需要开启自动注入。在SpringBoot下,我们只要使用了@Component ,@Bean和@Autowired的组合标签即可实现Bean的自动注入。那Spring又是如何帮我们关联上Bean的呢?
生成context
要了解这个问题,我们还是需要从SpringBoot的启动过程说起。
在Boot的run方法中,对帮我们创建出一个上下文context对象,来看下这个createApplicationContext方法:
而applicationContextFactory又是这样定义的:
根据DEFAULT的定义,最终帮我们生成的context是AnnotationConfigServletWebServerApplicationContext:
其构造方法如下:
在SpringBoot帮我们创建AnnotationConfigServletWebServerApplicationContext时,会去构建AnnotatedBeanDefinitionReader对象,在其构造方法内会去调用registerAnnotationConfigProcessors方法:
而在registerAnnotationConfigProcessors方法内
在这边会完成BeanName=“org.springframework.context.annotation.internalConfigurationAnnotationProcessor” 和ConfigurationClassPostProcessor的绑定。
下面我们再来看下AnnotationConfigServletWebServerApplicationContext构造器里的scanner属性的赋值逻辑:
我们可以看到,这边会调用ClassPathBeanDefinitionScanner的构造方法:
在构造器内部又调用了registerDefaultFilters方法,一起来看下:
可以看到,将Component 这个注解加入到了includeFilters里面。看includeFilters的取名,应该是需要包含Component这个注解的意思。ok,到这边我们已经将Boot帮我们创建的BeanFactory看完了。
Spring刷新
现在我们需要将目光放回Spring启动的流程了,Spring的大体启动流程,详情请见上一篇。
在上一章节中我们有讲到,invokeBeanFactoryPostProcessors会执行BeanFactoryProcesser,对BeanDefinition进行增强,现在我们简单的看会下这个方法:
可以看到,首先会调用invokeBeanFactoryPostProcessors方法,一起来看下:
在这个方法中,会调用getBeanNamesForType获取当前BeanFactory中实现了BeanDefinitionRegistryPostProcessor接口的BeanFactoryPostProcesser。还记得在SpringBoot帮我们创建context中会将BeanName=“org.springframework.context.annotation.internalConfigurationAnnotationProcessor” 和ConfigurationClassPostProcessor进行绑定吗?我们来看下ConfigurationClassPostProcessor的类层次结构图:
所以此时postProcessorNames中只会有一个变量:org.springframework.context.annotation.internalConfigurationAnnotationProcessor。
下面就是执行invokeBeanDefinitionRegistryPostProcessors方法了:
这里面的postProcessor,实际就是ConfigurationClassPostProcessor,所以这边调用的也就是ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法:
在方法内部,又会去调用processConfigBeanDefinitions方法,看名次,处理配置了的BeanDefinition,看下processConfigBeanDefinitions方法:
方法内部开始的for循环,第一次进入时会选出我们main方法所在的class对的BeanDefinition,放入candidates的set中。再后面会执行parse方法。
先简单看下checkConfigurationClassCandidate是如何判断注解是否可用的:
会依据这个顺序判断当前BeanDefinition的注解是否是可识别的注解。
现在我来看下parse方法:
这边我们可以看到一堆对SpringBoot中注解的处理。我们以ComponentScan注解处理为例:会调取到ClassPathScanningCandidateComponentProvider的 scanCandidateComponents方法:
在这个方法中,会加载我们在Spring boot上配置的@ComponentScan下的路径,默认是以main方法所在路径。
获取到扫描路径之后,会加载该路径下的所有文件,然后一个一个文件去遍历,如果通过了isCandidateComponent方法的检测,才会保留下来。我们来看下这个方法:
还记得SpringBoot在帮我们创建context时,在设置scanner属性的时候,会将Component注解放入includeFilters吗?现在,在isCandidateComponent方法中,就会检测当前扫描出来的class是否有注解,且其注解是否存在于includeFilters中。这样就可以将当前路径下有Component注解的class找出来了。
SpringBoot注解
通过上面的介绍,我们可以得知,SpringBoot在启动时,会出发Spring的BeanFactoryPostProcessor,然后会根据当前路径找到有相应注解的Class。
下面我们再来看下Boot的@SpringBootApplication注解:
再来看看@EnableAutoConfiguration注解“
所以当出发Spring的注解解析之后,一定会加载AutoConfigurationImportSelector这个Class,在这个类内部有方法:
后面会根据EnableAutoConfiguration这个Bean去spring.factory中加载一大堆的Class: