曹工說Spring Boot源碼(30)– ConfigurationClassPostProcessor 實在太硬核了,為了了解它,我可能debug了快一天
- 2020 年 7 月 25 日
- 筆記
- spring/boot源碼解析
寫在前面的話
相關背景及資源:
曹工說Spring Boot源碼(1)– Bean Definition到底是什麼,附spring思維導圖分享
曹工說Spring Boot源碼(2)– Bean Definition到底是什麼,咱們對着接口,逐個方法講解
曹工說Spring Boot源碼(3)– 手動註冊Bean Definition不比遊戲好玩嗎,我們來試一下
曹工說Spring Boot源碼(4)– 我是怎麼自定義ApplicationContext,從json文件讀取bean definition的?
曹工說Spring Boot源碼(5)– 怎麼從properties文件讀取bean
曹工說Spring Boot源碼(6)– Spring怎麼從xml文件里解析bean的
曹工說Spring Boot源碼(7)– Spring解析xml文件,到底從中得到了什麼(上)
曹工說Spring Boot源碼(8)– Spring解析xml文件,到底從中得到了什麼(util命名空間)
曹工說Spring Boot源碼(9)– Spring解析xml文件,到底從中得到了什麼(context命名空間上)
曹工說Spring Boot源碼(10)– Spring解析xml文件,到底從中得到了什麼(context:annotation-config 解析)
曹工說Spring Boot源碼(11)– context:component-scan,你真的會用嗎(這次來說說它的奇技淫巧)
曹工說Spring Boot源碼(12)– Spring解析xml文件,到底從中得到了什麼(context:component-scan完整解析)
曹工說Spring Boot源碼(13)– AspectJ的運行時織入(Load-Time-Weaving),基本內容是講清楚了(附源碼)
曹工說Spring Boot源碼(14)– AspectJ的Load-Time-Weaving的兩種實現方式細細講解,以及怎麼和Spring Instrumentation集成
曹工說Spring Boot源碼(15)– Spring從xml文件里到底得到了什麼(context:load-time-weaver 完整解析)
曹工說Spring Boot源碼(16)– Spring從xml文件里到底得到了什麼(aop:config完整解析【上】)
曹工說Spring Boot源碼(17)– Spring從xml文件里到底得到了什麼(aop:config完整解析【中】)
曹工說Spring Boot源碼(18)– Spring AOP源碼分析三部曲,終於快講完了 (aop:config完整解析【下】)
曹工說Spring Boot源碼(19)– Spring 帶給我們的工具利器,創建代理不用愁(ProxyFactory)
曹工說Spring Boot源碼(20)– 碼網恢恢,疏而不漏,如何記錄Spring RedisTemplate每次操作日誌
曹工說Spring Boot源碼(21)– 為了讓大家理解Spring Aop利器ProxyFactory,我已經拼了
曹工說Spring Boot源碼(22)– 你說我Spring Aop依賴AspectJ,我依賴它什麼了
曹工說Spring Boot源碼(23)– ASM又立功了,Spring原來是這麼遞歸獲取註解的元註解的
曹工說Spring Boot源碼(24)– Spring註解掃描的瑞士軍刀,asm技術實戰(上)
曹工說Spring Boot源碼(25)– Spring註解掃描的瑞士軍刀,ASM + Java Instrumentation,順便提提Jar包破解
曹工說Spring Boot源碼(26)– 學習位元組碼也太難了,實在不能忍受了,寫了個小小的位元組碼執行引擎
曹工說Spring Boot源碼(27)– Spring的component-scan,光是include-filter屬性的各種配置方式,就夠玩半天了
曹工說Spring Boot源碼(28)– Spring的component-scan機制,讓你自己來進行簡單實現,怎麼辦
曹工說Spring Boot源碼(29)– Spring 解決循環依賴為什麼使用三級緩存,而不是二級緩存
工程結構圖:
本篇前言
本篇是單獨基於spring-boot 2.1.7.RELEASE的版本寫的,本來沒有這篇文章的,本來正在寫遇到的一個eureka client的問題,然後有一個eureka的自動配置類,我當時準備講解一下:
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = {
"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration"})
public class EurekaClientAutoConfiguration {
結果,我發現,對於這一坨註解的執行順序,我並不是很了解,本來以為是spring.factories里配置了這個類,因此最早的入口是在那裡,結果,實際debug起來,發現好像並不是,而是由另外一個eureka的自動配置類觸發的。
因此,糾結半天,乾脆好好好好學研究下spring boot/cloud下configuration類的處理過程。
測試代碼
就是一個普通的spring boot下的eureka client程序,pom大致如下
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web </artifactId>
</dependency>
</dependencies>
一個誤會
一點點常識,大家可能都知道ConfigurationClassPostProcessor,這個類,負責處理各種@configuration註解配置的類(full模式),也包括輕量模式下的配置類(沒有@configuration配置,但是有@bean方法等)。
ConfigurationClassPostProcessor實現了如下接口:
實現了BeanDefinitionRegistryPostProcessor,總體來說,是對beanDefinition進行各種後置處理,比如增刪改beanDefinition。
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
這個方法,就是對beanFactory進行後置處理,而後置處理主要幹啥呢,就是增加beanDefinition,比如我們一個類A上,註解@configuration,同時註解@Import,導入了其他類。
那麼,就在這個方法中,就會去掃描configuration配置的類,比如掃描到類A,然後去獲取類A上的註解,然後遞歸獲取類A上的註解的元註解,最終檢查其中:是否有PropertySource、是否有ComponentScan、是否有Import、是否有@bean方法等等,去獲取更多的beanDefinition回來,並註冊到beanFactory。
因此,入口基本就是在 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
。
因此,我把斷點打在 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
,準備把這個各種註解的處理順序搞清楚。
結果,我跟了大半天,還了解了:
在spring cloud下,是有兩個applicationContext(如果有feign調用,會有更多,這裡暫不考慮)。
其中一個,就是bootStrap applicationContext;另外一個,才是應用程序本身的applicationContext。
而且,bootStrap applicationContext 是應用本身的applicationContext的parent。
我一開始沒注意到有兩個,因為我以為只有配置了bootStrap.yml才會有;結果跟了很久,都沒到我的應用的類,才意識到這個問題。
所以呢,跟了半天多的東西,其實是bootStrap applicationContext的東西,不過代碼邏輯都是一樣的;而且,學習bootStrap applicationContext也很有必要。
let『s start
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
/**
* Derive further bean definitions from the configuration classes in the registry.
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
...
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
這裡沒多少東西,主要就是最後一行開始:
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
該方法比較長,其實是ConfigurationClassPostProcessor太核心了,幾乎是spring boot的基石,所以只能分為多個部分來順序講解。
獲取候選bean集合
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
斷點顯示,這裡獲取到了,如下candidate:
過濾出configuration註解的類
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
上面的幾個候選類,經過這裡篩選後,只剩下一個滿足條件的bean。
bootstrapImportSelectorConfiguration
生成configuration類解析器
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
這個類,沒有繼承任何類,也沒有實現任何接口
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
// 1
this.componentScanParser = new ComponentScanAnnotationParser(
environment, resourceLoader, componentScanBeanNameGenerator, registry);
// 2
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
這裡1處,new了一個bean掃描解析器。
public ComponentScanAnnotationParser(Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) {
this.environment = environment;
this.resourceLoader = resourceLoader;
this.beanNameGenerator = beanNameGenerator;
this.registry = registry;
}
2處,創建了一個condition計算器,負責各種@condition的解析計算。
public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,
@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {
this.context = new ConditionContextImpl(registry, environment, resourceLoader);
}
使用ConfigurationClassParser循環解析
do {
// 1
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
接下來,先進入1處。
ConfigurationClassParser#parse
注意,進入此處時,參數configCandidates的值為:
該holder中,就包含beanName和beanDefinition,其中bean對應的class類型為:
org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 1
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
}
this.deferredImportSelectorHandler.process();
}
這裡會進入1處。
在進入該方法前,獲取了beanDefinition中的MetaData。
(AnnotatedBeanDefinition) bd).getMetadata()
protected final void parse(AnnotationMetadata metadata, String beanName) {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
這裡先去new了一個ConfigurationClass。
public ConfigurationClass(AnnotationMetadata metadata, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = metadata;
this.resource = new DescriptiveResource(metadata.getClassName());
this.beanName = beanName;
}
這個類,主要是對於@configuration註解標註的類的封裝。
/**
* Represents a user-defined {@link Configuration @Configuration} class.
* Includes a set of {@link Bean} methods, including all such methods
* defined in the ancestry of the class, in a 'flattened-out' manner.
*
*/
final class ConfigurationClass {
開始解析
org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) {
// 1
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
1處,使用condition計算器,進行判斷,看看該bean是否滿足
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
因為org.springframework.cloud.bootstrap.BootstrapImportSelectorConfiguration
類上,並沒有condition註解,所以是默認生效的。
接下來進入下面的地方:
protected void processConfigurationClass(ConfigurationClass configClass){
// 0
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
// 1
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
return;
}
else {
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// 2
SourceClass sourceClass = asSourceClass(configClass);
do {
// 3
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
-
0,就是前面說的判斷condition是否滿足
-
1,此時不滿足條件,直接跳過
-
2,這裡根據註解信息,獲取sourceClass,不用細究
private SourceClass asSourceClass(ConfigurationClass configurationClass){ AnnotationMetadata metadata = configurationClass.getMetadata(); if (metadata instanceof StandardAnnotationMetadata) { return asSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass()); } return asSourceClass(metadata.getClassName()); }
-
3處,繼續解析。
這個類較長,我們下面細講。
doProcessConfigurationClass
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass){
// 3.1
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
//3.2 Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}
這裡實際上,會進入3.1處。因為這個類上,加了@configuration註解的。
@Configuration
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {
}
處理member類
然後3.2處,member類處理,這裡暫時不太清楚member類是什麼,不過我們這個BootstrapImportSelectorConfiguration
也沒有獲取到任何的member class,所以先跳過。
處理PropertySource
接下來,開始解析bean的class上,是否註解了PropertySource.
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
}
這裡,我們並沒有註解PropertySource,所以也會跳過。
處理componnet-scan
這裡也沒有,跳過。
處理@imort
processImports(configClass, sourceClass, getImports(sourceClass), true);
在processImports之前,這裡第三個參數,先去調用了getImports。
getImports
/**
* Returns {@code @Import} class, considering all meta-annotations.
*/
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<>();
Set<SourceClass> visited = new LinkedHashSet<>();
collectImports(sourceClass, imports, visited);
return imports;
}
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
throws IOException {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
// 1
collectImports(annotation, imports, visited);
}
}
// 2
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
-
1,遞歸調用自己,獲取@Import註解
-
2,將@import註解中value的值取出來,放到imports中。
這裡處理完成後,我們獲取到的東西如下:
即:
org.springframework.cloud.bootstrap.BootstrapImportSelector
processImport
processImports(configClass, sourceClass, getImports(sourceClass), true);
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
this.importStack.push(configClass);
try {
// 0
for (SourceClass candidate : importCandidates) {
// 1
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 2
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 3
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
}
- 1處,當要import的是ImportSelector接口時
- 2處,當要import的bean class是:ImportBeanDefinitionRegistrar
- 3處,當要import的是普通的configuration class時。
我們這裡這個類,是實現了DeferredImportSelector
,間接實現了ImportSelector
:
public class BootstrapImportSelector implements EnvironmentAware, DeferredImportSelector
所以要進入下面這一坨邏輯:
for (SourceClass candidate : importCandidates) {
// 1
if (candidate.isAssignable(ImportSelector.class)) {
//2 Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
// 3
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
// 4
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
// 5
if (selector instanceof DeferredImportSelector) {
// 6
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
- 1, 判斷如果是實現了ImportSelector
- 2,加載對應的bean class
- 3,通過反射實例化該bean
- 4,調用aware方法,注入environment等
- 5,判斷是否為DeferredImportSelector,該類型需要被延遲import
- 6,處理該DeferredImportSelector
6處,使用專門的handler,來處理DeferredImportSelector類型的bean。
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
// 1
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
// 2
this.deferredImportSelectors.add(holder);
}
}
-
1,將configClass,和importSelector放進一個holder中。
public DeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector) { this.configurationClass = configClass; this.importSelector = selector; }
-
2,往如下的list中,添加一個holder實例。
@Nullable private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
到這裡,基本@import就處理完了,因為前面這個importSelector是deferred類型,是需要延期處理的,所以,加入該list後,處理結束。
處理@bean方法
這裡沒有bean方法,跳過。
處理接口中的默認方法
這個暫時不涉及,跳過。
處理deferredImportSelector
org.springframework.context.annotation.ConfigurationClassParser#parse
public void parse(Set<BeanDefinitionHolder> configCandidates) {
// 0
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
// 1
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
}
// 2
this.deferredImportSelectorHandler.process();
}
接下來,我們回到之前的代碼,1處的parse方法終於處理結束了,本來應該進入0處的下一輪循環,但是這裡因為集合中只有那麼一個元素:bootstrapImportSelectorConfiguration。所以這步就算處理完了。
進入到2處。
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
//1
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
// 2
deferredImports.forEach(handler::register);
// 3
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
- 1,new了一個handler,專門處理這種延遲導入的bean selector
- 2,對需要延遲導入的bean selector,進行遍歷,然後調用handler的register
- 3,調用handler的批量import方法。
我們對2處和3處重點講解。
handler::registered
public void register(DeferredImportSelectorHolder deferredImport) {
// 0
Class<? extends Group> group = deferredImport.getImportSelector()
.getImportGroup();
// 1
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 2
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
-
0,從holder二元組中,獲取importSelector,然後獲取其importGroup。
這裡的group為null。
public interface DeferredImportSelector extends ImportSelector { /** * Return a specific import group. * <p>The default implementations return {@code null} for no grouping required. * @return the import group class, or {@code null} if none * @since 5.0 */ @Nullable default Class<? extends Group> getImportGroup() { return null; }
-
1處,比較複雜。
這裡有個field:
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
1處我們可以看出,是在往上面這個map,放東西。
key:
(group != null ? group : deferredImport)
因為我們這裡group為null,所以這裡的key為:
DeferredImportSelectorHolder deferredImport
,也就是那個二元組。value是啥呢?
key -> new DeferredImportSelectorGrouping(createGroup(group))
我們先看看createGroup吧:
private Group createGroup(@Nullable Class<? extends Group> type) { // 1 Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class); Group group = BeanUtils.instantiateClass(effectiveType); ParserStrategyUtils.invokeAwareMethods(group, ConfigurationClassParser.this.environment, ConfigurationClassParser.this.resourceLoader, ConfigurationClassParser.this.registry); return group; }
1處,因為我們傳入的參數:type為null,所以這裡場景了一個DefaultDeferredImportSelectorGroup的實例,填充Aware字段後,返回。
然後,我們利用createGroup返回的實例,傳給了:
key -> new DeferredImportSelectorGrouping(createGroup(group))
然後看看這個類呢:
private static class DeferredImportSelectorGrouping { private final DeferredImportSelector.Group group; private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>(); // 1 DeferredImportSelectorGrouping(Group group) { this.group = group; }
-
2,我們上面一步,往map里放了個key、value。
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
-
3,現在需要往value(類型為DeferredImportSelectorGrouping),加入一個延遲importSelector的holder
public void register(DeferredImportSelectorHolder deferredImport) { Class<? extends Group> group = deferredImport.getImportSelector() .getImportGroup(); DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); // grouping.add(deferredImport); this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); }
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#add public void add(DeferredImportSelectorHolder deferredImport) { this.deferredImports.add(deferredImport); }
-
註冊
public void register(DeferredImportSelectorHolder deferredImport) { Class<? extends Group> group = deferredImport.getImportSelector() .getImportGroup(); DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); // 4 this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); }
然後進入到上面的4處,這裡把這個延遲importSelector的metadata作為key,configurationClass作為value,放進map。
private class DeferredImportSelectorGroupingHandler { private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>(); // 1 private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
即上面1處這個map。
進行group import
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
// 0
deferredImports.forEach(handler::register);
// 1
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
前面已經把0處,講解完畢;這裡進入1處。
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
private Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
public void processGroupImports() {
// 1
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
// 2
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
});
}
}
-
1處,我們這裡遍歷groupings這個map的value集合
-
2,獲取這個grouping中的要import的集合
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#getImports private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>(); /** * Return the imports defined by the group. * @return each import with its associated configuration class */ public Iterable<Group.Entry> getImports() { // 1 for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { // 2 this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this.group.selectImports(); }
- 1,遍歷全部的holder
- 2,獲取holder中的,這個importSelector的類的元數據,和importSelector本身,傳給this.group.process方法。
我們看看這裡的process方法
org.springframework.context.annotation.ConfigurationClassParser.DefaultDeferredImportSelectorGroup private static class DefaultDeferredImportSelectorGroup implements Group { private final List<Entry> imports = new ArrayList<>(); @Override public void process(AnnotationMetadata metadata, DeferredImportSelector selector) { // 1 for (String importClassName : selector.selectImports(metadata)) { this.imports.add(new Entry(metadata, importClassName)); } } @Override public Iterable<Entry> selectImports() { return this.imports; } }
這裡的1處,即調用了selector接口的方法了
public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */ String[] selectImports(AnnotationMetadata importingClassMetadata); }
1處的selector.selectImports,我們可以看到,傳進去了一個metadata,這個metaData都有啥數據呢?
我們再看一眼下面這個類:
@Configuration @Import(BootstrapImportSelector.class) public class BootstrapImportSelectorConfiguration { }
所以,傳入的metaData就是這個被@Import註解,註解了的類的信息。
相當於說,你在類A上加上@Import註解,那麼最終類A的信息,會被當做參數,傳給ImportSelector的selectImports方法。
BootstrapImportSelector
前面說到了這個selector實現了DeferredImportSelector,我們看看怎麼實現的吧:
public class BootstrapImportSelector implements EnvironmentAware, DeferredImportSelector {
private Environment environment;
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 1
List<String> names = new ArrayList<>(SpringFactoriesLoader
.loadFactoryNames(BootstrapConfiguration.class, classLoader));
// 2
names.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(
this.environment.getProperty("spring.cloud.bootstrap.sources", ""))));
// 3
List<OrderedAnnotatedElement> elements = new ArrayList<>();
for (String name : names) {
try {
elements.add(
// 4
new OrderedAnnotatedElement(this.metadataReaderFactory, name));
}
catch (IOException e) {
continue;
}
}
AnnotationAwareOrderComparator.sort(elements);
String[] classNames = elements.stream().map(e -> e.name).toArray(String[]::new);
// 5
return classNames;
}
-
1,從spring.factories中,查找以
org.springframework.cloud.bootstrap.BootstrapConfiguration
為key的property。我們目前這個代碼中,在如下文件,找到了一處:
然後在eureka的jar包,找到一個:
所以,我們拿到了5個值。
-
2處,從spring.cloud.bootstrap.sources屬性中獲取
-
3處,遍歷所有這些要import的類名
-
4處,將類名轉換為OrderedAnnotatedElement,這個會獲取對應的類的元數據,然後獲取其上註解的@order來獲取順序
OrderedAnnotatedElement(MetadataReaderFactory metadataReaderFactory, String name) throws IOException { MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(name); AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); Map<String, Object> attributes = metadata .getAnnotationAttributes(Order.class.getName()); this.name = name; if (attributes != null && attributes.containsKey("value")) { this.value = (Integer) attributes.get("value"); this.order = new Order() { @Override public Class<? extends Annotation> annotationType() { return Order.class; } @Override public int value() { return OrderedAnnotatedElement.this.value; } }; } }
-
5處返回排序後的,要import的class的類名。
將要import的類名,存放起來
private static class DefaultDeferredImportSelectorGroup implements Group {
private final List<Entry> imports = new ArrayList<>();
@Override
public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
// 1
for (String importClassName : selector.selectImports(metadata)) {
// 2
this.imports.add(new Entry(metadata, importClassName));
}
}
@Override
public Iterable<Entry> selectImports() {
return this.imports;
}
}
前面講完了1處,現在看看2處。
2處就是將前面拿到的5個要import的類,加入到這裡的imports 集合中。
此時,imports集合如下:
遞歸處理下一個configuration class
上面我們獲取到了5個要import的class。
org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
// 1
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
});
}
}
這裡1處的grouping.getImports,就能拿到那5個元素。
這裡又去開始循環處理,看下圖。
處理PropertySourceBootstrapConfiguration
我們看看這個類
@Configuration
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// 1
...
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 2
...
}
else {
// 3 process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 4
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
}
因為其沒有實現ImportSelector等,所以進入3處,當做普通的Configuration類處理。
private static class ImportStack extends ArrayDeque<ConfigurationClass> implements ImportRegistry {
private final MultiValueMap<String, AnnotationMetadata> imports = new LinkedMultiValueMap<>();
//
public void registerImport(AnnotationMetadata importingClass, String importedClass) {
// 1
this.imports.add(importedClass, importingClass);
}
這裡直接把其放到map中。
然後進入了前面的4處:
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 4
processConfigurationClass(candidate.asConfigClass(configClass));
}
org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 1
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
...
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
// 2
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
和之前一樣,這裡,1處,判斷是否滿足condition註解,因為我們的PropertySourceBootstrapConfiguration,並沒有condition,所以是默認生效的。
處理member類
不涉及。
處理PropertySource註解
不涉及。
處理ComponentScan註解
不涉及
處理import註解
由於該類上,加了
@Configuration
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration
而:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesImportSelector.class)
public @interface EnableConfigurationProperties {
所以,處理這裡時:
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
在getImports調用,得到如下返回。
org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector
然後開始處理該import。
由於其實現了ImportSelector,會進入下面的地方。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
//1 Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 2
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
-
1,反射創建該selector
-
2,調用該selector的selectImport方法,得到要import的類
class EnableConfigurationPropertiesImportSelector implements ImportSelector { private static final String[] IMPORTS = { ConfigurationPropertiesBeanRegistrar.class.getName(), ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() }; @Override public String[] selectImports(AnnotationMetadata metadata) { return IMPORTS; }
這裡,我們就拿到了2個要import的類的類名。
接下來,又開始對這兩個要import的類,進行處理。
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
// 1
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
即上面的1這一處地方,進行遞歸處理,此時要import的兩個類,是這樣的:
咱們這裡不展開了,沒完了。。
處理ImportResource註解
不涉及
處理bean方法
不涉及
處理EncryptionBootstrapConfiguration
@Configuration
@ConditionalOnClass({ TextEncryptor.class })
@EnableConfigurationProperties({ KeyProperties.class })
public class EncryptionBootstrapConfiguration {
這個類,大家看看就好。沒有新東西,不會說再去import什麼東西。
不過這個類上就有condition條件了。
在如下方法時,使用condition計算器,就會發現真的有一個condition要計算。
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
然後就又是同樣流程,處理member、處理PropertySource、ComponentScan等等。
跳過後續的3個configuration類的處理
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
這些都跳過,道理類似的。
parse完成後的後續處理
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
...
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 1
parser.parse(candidates);
// 2
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
...
}
整個過程比較複雜,我們這裡分析了那麼多,主要是把1處的代碼說的差不多了。
2處,加載beanDefinition。
經過這個步驟後,beanFactory中的bean如下:
總結
到此的話,幾乎差不多吧,細節還是很多,有些地方肯定沒講到,後續再補上。
demo的源碼本身很簡單,如果大家需要,可以從這裡獲取:
//gitee.com/ckl111/all-simple-demo-in-work-1/tree/master/eureka/