1. 程式人生 > 實用技巧 >spring 原始碼學習四:ConfigurationClassPostProcessor @Import註解解析

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();
}
}
}

最終在processConfigurationClassthis.configurationClasses.put(configClass, configClass); 將解析好的配置類以map的形式儲存起來,這樣spring在後續的呼叫就可以獲取該map集合的配置資訊。