spring 源码学习四:ConfigurationClassPostProcessor @Import注解解析
spring 源码学习四:ConfigurationClassPostProcessor @Import注解解析
不知道各位小伙伴在面试过程中有没有被问到spring-boot 自动装配原理,博主在面试中恰好也有被问到。然后我的回答是:
在@SpringBootApplication这个组合注解中,其中有一个@EnableAutoConfiguration注解,显示通过@Import注入AutoConfigurationImportSelector.class,该class中通过getCandidateConfigurations会到classpath下的读取META-INF/spring.factories文件的配置,并返回一个字符串数组。最终这个数组就是spring原生自动装配的组件信息。如下图


当时对这个也是一知半解,面试官听了我的回答他也没说错,后续就问到spring是如何解析这些配置信息的,我瞬间就懵逼了。这个问题没答上最终归根于对spring底层不了解。及时面试的时候问到是spring-boot的原理,但底层也是对spring的扩展
@Import解析原理
首先在了解@Import解析原理之前,必须得了解AbstractApplicationContext 的refresh()中invokeBeanFactoryPostProcessors beanFactory后置处理器,
这个方法中就有对注解进行解析过程,本篇只针对@Import注解解析

1、首先进入invokeBeanFactroyPostPorcessors 获取到当前应用程序上下文的beanFactoryPostProcessors变量的值,并且实例化调用执行所有已经注册的beanFactoryPostProcessor;
2、PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors 主要是针对beanFactor和beanDefition根据不同对实现做不同对集合处理,然后进入invokeBeanDefinitionRegistryPostProcessors

3、ConfigurationClassPostProcessor对postProcessBeanDefitnionRegsity实现中就有对配置类信息进行处理

4、最终在processConfigurationClass中doProcessConfigurationClass解析各种注解

本篇是针对@import做解析的,其它的配置解析后续再补充:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 如果使用@Import注解修饰的类集合为空,那么直接返回
if (importCandidates.isEmpty()) {
return;
}
// 通过一个栈结构解决循环引入
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 添加到栈中,用于处理循环引入的问题
this.importStack.push(configClass);
try {
// 遍历每一个@Import注解的类
for (SourceClass candidate : importCandidates) {
// 检验配置类Import引入的类是否是ImportSelector子类
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
// 候选类是一个导入选择器->委托来确定是否进行导入
Class<?> candidateClass = candidate.loadClass();
// 通过反射生成一个ImportSelect对象
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
// 获取选择器的额外过滤器
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
// 判断引用选择器是否是DeferredImportSelector接口的实例
// 如果是则应用选择器将会在所有的配置类都加载完毕后加载
if (selector instanceof DeferredImportSelector) {
// 将选择器添加到deferredImportSelectorHandler实例中,预留到所有的配置类加载完成后统一处理自动化配置类
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 获取引入的类,然后使用递归方式将这些类中同样添加了@Import注解引用的类
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 递归处理,被Import进来的类也有可能@Import注解
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// 如果是实现了ImportBeanDefinitionRegistrar接口的bd
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
// 候选类是ImportBeanDefinitionRegistrar -> 委托给当前注册器注册其他bean
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
/**
* 放到当前configClass的importBeanDefinitionRegistrars中
* 在ConfigurationClassPostProcessor处理configClass时会随之一起处理
*/
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
// 候选类既不是ImportSelector也不是ImportBeanDefinitionRegistrar-->将其作为@Configuration配置类处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
/**
* 如果Import的类型是普通类,则将其当作带有@Configuration的类一样处理
* 将candidate构造为ConfigurationClass,标注为importedBy,意味着它是通过被@Import进来的
* 后面处理会用到这个判断将这个普通类注册进DefaultListableBeanFactory
*/
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
最终在processConfigurationClass this.configurationClasses.put(configClass, configClass); 将解析好的配置类以map的形式存储起来,这样spring在后续的调用就可以获取该map集合的配置信息。

浙公网安备 33010602011771号