本文以SpringBoot應用爲基礎,嘗試分析基於註解@Configuration
的配置類是如何向Spring容器註冊BeanDefinition的過程java
其中主要分析了 ConfigurationClassPostProcessor
這個BeanDefinitionRegistryPostProcessor
即Bean定義註冊後置處理器,在Spring啓動過程當中對@Configuration配置類的處理,主要體如今 解析並發現全部配置類,處理配置類的相關邏輯(如配置類上的@ComponentScan、@Import、@Bean註解等),註冊其中的BeanDefinitionspring
SpringBoot版本:2.0.9.RELEASE併發
Spring版本:5.0.13.RELEASEide
首先看一下ConfigurationClassPostProcessor的類繼承關係
函數
從紅框中能夠看出ConfigurationClassPostProcessor
是BeanDefinitionRegistryPostProcessor
接口的實現類,便是一個Bean定義註冊的後置處理器,會在Spring容器啓動時被調用,具體時機爲工具
// 調用鏈 AbstractApplicationContext.refresh() => invokeBeanFactoryPostProcessors() => PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()
invokeBeanFactoryPostProcessors()
會先調用全部的BeanDefinitionRegistryPostProcessor
以後,再調用全部的BeanFactoryPostProcessor
源碼分析
ConfigurationClassPostProcessor又是如何被引入Spring的呢??post
SpringBoot應用會在ApplicationContext應用上下文被建立的構造函數中new AnnotatedBeanDefinitionReader
這個用於註冊基於註解的BeanDefinition的Reader,在其構造中又會調用AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)
使用工具類向Spring容器中註冊一些所謂的註解配置處理器,其中就包含ConfigurationClassPostProcessor
ui
// ConfigurationClassPostProcessor被註冊 AnnotationConfigServletWebServerApplicationContext構造 => new AnnotatedBeanDefinitionReader(registry) => AnnotationConfigUtils.registerAnnotationConfigProcessors(registry) => 註冊ConfigurationClassPostProcessor到Spring容器
首先,ConfigurationClassPostProcessor後置處理器的處理入口爲postProcessBeanDefinitionRegistry()
方法。其主要使用了ConfigurationClassParser
配置類解析器解析@Configuration
配置類上的諸如@ComponentScan
、@Import
、@Bean
等註解,並嘗試發現全部的配置類;還使用了ConfigurationClassBeanDefinitionReader
註冊所發現的全部配置類中的全部Bean定義;結束執行的條件是全部配置類都被發現和處理,相應的bean定義註冊到容器this
大體流程以下:
一、經過BeanDefinitionRegistry查找當前Spring容器中全部BeanDefinition
二、經過ConfigurationClassUtils.checkConfigurationClassCandidate()
檢查BeanDefinition是否爲 「徹底配置類」 或 「簡化配置類」,並對配置類作標記,放入集合待後續處理
Spring配置類的分類能夠 參考
三、經過 ConfigurationClassParser解析器
parse解析配置類集合,嘗試經過它們找到其它配置類
四、使用 ConfigurationClassBeanDefinitionReader
註冊經過所發現的配置類中找到的全部beanDefinition
五、處理完一輪配置類後,查看BeanDefinitionRegistry中是否存在新加載的且還未被處理過的 「徹底配置類」 或 「簡化配置類」,有的話繼續上面步驟
其中第三、4步後面重點分析
對於SpringBoot應用來講,參與解析的種子配置文件即爲SpringBoot的Application啓動類
經過ConfigurationClassParser解析器parse解析配置類集合,嘗試經過它們找到其它配置類
循環解析全部配置類 ConfigurationClassParser#processConfigurationClass()
根據@Conditional的ConfigurationPhase.PARSE_CONFIGURATION階段條件判斷是否跳過配置類
注意:有些@Conditional是在當前這個PARSE_CONFIGURATION解析配置階段使用的,有些是在REGISTER_BEAN註冊beanDefinition階段使用的
【重點】調用ConfigurationClassParser#doProcessConfigurationClass()循環解析配置類,直到不存在未處理過的父類
processDeferredImportSelectors():處理推遲的ImportSelector集合,其實就是延遲調用了processImports()
SpringBoot的自動配置類就是被DeferredImportSelector推遲導入的
ConfigurationClassParser#processConfigurationClass()
包含了處理單個配置類的大致流程,先根據ConfigurationPhase.PARSE_CONFIGURATION
解析配置階段的@Conditional
條件判斷當前配置類是否應該解析,以後調用ConfigurationClassParser#doProcessConfigurationClass()
循環解析配置類,直到不存在未處理過的父類
/** * 解析單個配置類 * 解析的最後會將當前配置類放到configurationClasses */ protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { /** * 根據@Conditional條件判斷是否跳過配置類 * 注意:當前這個PARSE_CONFIGURATION解析配置階段只會使用這個階段的@Conditional條件,有些REGISTER_BEAN註冊beanDefinition階段的條件不會在此時使用 */ if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); // 若是configClass在已經分析處理的配置類記錄中已存在 if (existingClass != null) { //若是配置類是被@Import註冊的,return if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } // 不然,清除老的記錄,在來一遍 else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // Recursively process the configuration class and its superclass hierarchy. /** * 遞歸處理配置類及其超類層次結構 * 從當前配置類configClass開始向上沿着類繼承結構逐層執行doProcessConfigurationClass,直到遇到的父類是由Java提供的類結束循環 */ SourceClass sourceClass = asSourceClass(configClass); /** * 循環處理配置類configClass直到sourceClass變爲null,即父類爲null * doProcessConfigurationClass的返回值是其參數configClass的父類 * 若是該父類是由Java提供的類或者已經處理過,返回null */ do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
ConfigurationClassParser#doProcessConfigurationClass():真正解析配置類
經過解析配置類上的註解、內部成員類和方法構建一個完整的ConfigurationClass配置類,過程當中若是發現了新的配置類能夠重複調用此方法
真正解析過程當中會處理成員內部類
、@PropertySource
、@ComponentScan
、@Import
、@ImportSource
、@Bean方法
等,流程以下:
/** * 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) throws IOException { // Recursively process any member (nested) classes first /** * 一、處理配置類的成員類(配置類內嵌套定義的類) * 內部嵌套類也多是配置類,遍歷這些成員類,檢查是否爲"徹底/簡化配置類" * 有的話,調用processConfigurationClass()處理它們,最終將配置類放入configurationClasses集合 */ processMemberClasses(configClass, sourceClass); // Process any @PropertySource annotations /** * 二、處理 @PropertySource * 將找到的PropertySource添加到environment的PropertySource集合 */ for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations /** * 三、處理 @ComponentScan * 處理用戶手工添加的@ComponentScan,SpringBoot建立ApplicationContext時的ClassPathBeanDefinitionScanner是爲了掃描啓動類下的包 * 爲的是找到知足條件的@ComponentScan,即@Component相關的組件,先掃描一下,掃描到的就註冊爲BeanDefinition * 看其中是否還有配置類,有的話parse()繼續分析處理,配置類添加到configurationClasses集合 */ Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); // 若是當前配置類上有@ComponentScan,且使用REGISTER_BEAN註冊beanDefinition的條件判斷也不跳過的話 if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately // 當即掃描,掃描到的就註冊爲BeanDefinition,並得到掃描到的全部beanDefinition // 在處理SpringBoot啓動類上的@ComponentScan時,雖然指指定了excludeFilters,但會根據啓動類所在包推測basePackage,就會掃描到SpringBoot啓動類包如下的Bean並註冊 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed // 檢查掃描到的beanDefinition中是否有配置類,有的話parse()繼續分析處理,,配置類添加到configurationClasses集合 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations /** * 四、處理 @Import * (1)處理ImportSelector * 若是是DeferredImportSelector,如SpringBoot的自動配置導入,添加到deferredImportSelectors,延遲進行processImports() * 其它經過ImportSelector找到的類,繼續調用processImports(),要麼是@Configuration配置類繼續解析,要麼是普通組件導入Spring容器 * (2)處理ImportBeanDefinitionRegistrar * 調用當前配置類的addImportBeanDefinitionRegistrar(),後面委託它註冊其它bean定義 * (3)其它 * 調用processConfigurationClass()繼續解析,最終要麼是配置類放入configurationClasses,要麼是普通組件導入Spring容器 */ processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations /** * 五、處理 @ImportResource * 添加到配置類的importedResources集合,後續loadBeanDefinitions()加載bean定義時再讓這些導入BeanDefinitionReader自行讀取bean定義 */ AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods /** * 六、處理個別@Bean方法 * 獲取全部@Bean方法,並添加到配置類的beanMethods集合 */ Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces /** * 七、處理配置類接口上的default methods */ processInterfaces(configClass, sourceClass); // Process superclass, if any /** * 八、檢查父類是否須要處理,若是父類須要處理返回父類,不然返回null * 若是存在父類,且不在knownSuperclasses已經分析過的父類列表裏,返回並繼續分析 */ if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }
在上面解析配置類的過程當中,除了構建了一個完整的ConfigurationClass配置類,其實已經向BeanDefinitionRegistry
中添加了一些beanDefinition了,好比在處理@ComponentScan
時,掃描到的@Component相關組件
就已經註冊了
而ConfigurationClassBeanDefinitionReader
會繼續讀取已經構建好的ConfigurationClass配置類中的成員變量,從而註冊beanDefinition
構建好的ConfigurationClass配置類中在本階段可用的成員變量包括:
Set<BeanMethod> beanMethods
: @Bean的方法Map<String, Class<? extends BeanDefinitionReader>> importedResources
:配置類上@ImportResource註解的類存入此集合,會使用BeanDefinitionReader讀取Resource中的BeanDefinition並註冊Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars
:ImportBeanDefinitionRegistrar集合經過構建好的配置類的配置信息,使用ConfigurationClassBeanDefinitionReader註冊全部可以讀取到的beanDefinition:
根據ConfigurationPhase.REGISTER_BEAN階段
條件判斷配置類是否須要跳過
循環判斷配置類以及導入配置類的類,使用ConfigurationPhase.REGISTER_BEAN階段條件判斷是否須要跳過只要配置類或導入配置類的類須要跳過即返回跳過
若是configClass.isImported(),將配置類自身註冊爲beanDefinition
註冊配置類全部@Bean方法
爲beanDefinition
註冊由@ImportedResources
來的beanDefinition,即經過其它類型Resource的BeanDefinitionReader
讀取BeanDefinition並註冊,如xml格式的配置源 XmlBeanDefinitionReader
註冊由ImportBeanDefinitionRegistrars
來的beanDefinition
/** * Read a particular {@link ConfigurationClass}, registering bean definitions * for the class itself and all of its {@link Bean} methods. * 讀取特定配置類,根據配置信息註冊bean definitions */ private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { /** * 根據ConfigurationPhase.REGISTER_BEAN階段條件判斷配置類是否須要跳過 * 循環判斷配置類以及導入配置類的類,使用ConfigurationPhase.REGISTER_BEAN階段條件判斷是否須要跳過 * 只要配置類或導入配置類的類須要跳過即返回跳過 */ if (trackedConditionEvaluator.shouldSkip(configClass)) { String beanName = configClass.getBeanName(); if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { this.registry.removeBeanDefinition(beanName); } this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); return; } // 一、若是當前配置類是經過內部類導入 或 @Import導入,將配置類自身註冊爲beanDefinition if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } // 二、註冊配置類全部@Bean方法爲beanDefinition for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } // 三、註冊由@ImportedResources來的beanDefinition // 即經過其它類型Resource的BeanDefinitionReader讀取BeanDefinition並註冊 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // 四、註冊由ImportBeanDefinitionRegistrars來的beanDefinition loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
Spring BeanDefinitionRegistryPostProcessor:ConfigurationClassPostProcessor