曹工說Spring Boot原始碼(30)-- ConfigurationClassPostProcessor 實在太硬核了,為了瞭解它,我可能debug了快一天

三國夢迴發表於2020-07-25

寫在前面的話

相關背景及資源:

曹工說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/

相關文章