寫在前面的話
相關背景及資源:
曹工說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的原始碼本身很簡單,如果大家需要,可以從這裡獲取:
https://gitee.com/ckl111/all-simple-demo-in-work-1/tree/master/eureka/