Spring註解解析之ConfigurationClassPostProcessor

概述

Spring中有各類註解,好比@Configuration、@Import、@ComponentScan、@Autowired等等,那Spring是怎麼識別和解析這些註解的呢。是經過BeanFactoryPostProcessor和BeanPostProcessor這兩種擴展的機制來實現解析和識別的。那麼咱們來認識下這些處理註解的各類BeanFactoryPostProcessor和BeanPostProcessor,包括:java

  • ConfigurationClassPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • RequiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor

它們都是經過方法AnnotationConfigUtils#registerAnnotationConfigProcessors()向容器中註冊的,所以咱們能夠得出這麼一個結論,不管是經過SpringBoot、xml文件的<context:/>擴展標籤仍是純JavaConfig形式的標準應用,最終底層都會調用這個方法向容器註冊這些BeanFactoryPostProcessor和BeanPostProcessor的,這樣才能識別這些註解起到配置的做用。ui

ConfigurationClassPostProcessor

ConfigurationClassPostProcessor會處理全部BeanDefinition中的符合註解條件的BeanDefinition,(@Configuration註解的、@Component、@ComponentScan、@Import、@ImportResource或者@Bean註解的),使用ConfigurationClassParser解析出javaconfig配置類,使用ConfigurationClassBeanDefinitionReader對解析出的結果進行加載。this

PS:ConfigurationClassPostProcessor是對全部註冊到Spring容器中的BeanDefinition進行處理,是已經註冊的是關鍵,無論是手動註冊仍是掃描機制。lua

一、過濾出帶有相關注解的BeanDefinition

對全部BeanDefinition進行篩查,符合條件的是帶有註解@Configuration、@Component、@ComponentScan、@Import、@ImportResource或者@Bean註解的。spa

else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
	configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}

二、對第一步的候選類進一步處理

帶有@Configuration註解的Bean Class上面的註解@ComponentScan、@Import、@ImportResource、@Bean這些註解是怎麼解析的呢,就是使用ConfigurationClassParser類來依依解析的。根據註解的不一樣進行不一樣的處理,目的是找出全部的配置類。咱們抽出重要的部分看下,對應方法爲processConfigurationClass()code

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
	// Recursively process any member (nested) classes first
    ...
	// Process any @PropertySource annotations
	...

	// Process any @ComponentScan annotations
	<1>、處理@ComponentScan註解

	// Process any @Import annotations
	<2>、處理@Import註解

	// Process any @ImportResource annotations
	<3>、處理@ImportResource註解

	// Process individual @Bean methods
	<4>、處理單獨的@Bean註解方法

	// Process superclass, if any
	...

	// No superclass -> processing is complete
	return null;
}

<1>、處理@ComponentScan

直接使用ClassPathBeanDefinitionScanner進行掃描,對掃描出的候選配置類在進行步驟2的處理component

// Process any @ComponentScan annotations
AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);
if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
	// The config class is annotated with @ComponentScan -> perform the scan immediately
	// 使用ClassPathBeanDefinitionScanner掃描
	Set<BeanDefinitionHolder> scannedBeanDefinitions =
			this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
	// Check the set of scanned definitions for any further config classes and parse recursively if necessary
	for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
		if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
			// 若是是配置類,遞歸處理
			parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
		}
	}
}

 

<2>、處理@Import

找出全部Import註解(包括註解的層級關係,父子註解)的屬性Class,根據Class的類型不一樣進行不一樣處理,對應方法processImports()orm

2.一、ImportSelectorxml

若是類型是ImportSelector,會建立ImportSelector的實例調用selectImports方法,將返回值做爲@Import註解的class屬性,遞歸調用processImports()對象

2.二、ImportBeanDefinitionRegistrar

若是類型是ImportBeanDefinitionRegistrar,建立ImportBeanDefinitionRegistrar的對象先加入importBeanDefinitionRegistrars屬性中

configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

2.三、其餘類型

若是不是,也不是,就把噹噹成@Configuration類型的,調用processConfigurationClass()從新處理

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 (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
			this.deferredImportSelectors.add(
					new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
		}
		else {
			String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
			Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
			processImports(configClass, currentSourceClass, importSourceClasses, false);
		}
	}
	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 {
		// 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));
	}
}

 

<3>、處理@ImportResource

分別獲取@ImportResource設置的location屬性,放入到importedResources

AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
	String[] resources = importResource.getStringArray("locations");
	Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
	for (String resource : resources) {
		String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
		configClass.addImportedResource(resolvedResource, readerClass);
	}
}

<4>、處理@Bean

帶有@Bean註解的方法,都加入到beanMethods

// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
	configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

三、解析出的結果進行加載

使用ConfigurationClassParser解析每一個帶有@Configuration註解的Bean的類以後,這個類上面的@ComponentScan、@Import、@ImportResource、@Bean這些註解都已經被解析過了。須要使用

ConfigurationClassBeanDefinitionReader來將解析的結果加載到Spring容器中。

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
		TrackedConditionEvaluator trackedConditionEvaluator) {

	//
	//

	
	if (configClass.isImported()) {
		// 一、這個表示這個Class是被其餘@Configuration的類導入的(使用@Import),就這個Class定義爲BeanDefinition註冊到Spring容器中
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	二、每一個@Bean註解的方法,解析@Bean的屬性,而且將這個方法定義爲BeanDefinition註冊到Spring容器中
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
	三、加載@ImportResource定義的配置文件,xml格式或者groovy格式
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	四、調用每一個ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,由ImportBeanDefinitionRegistrar來實現註冊的邏輯
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

說明:

  1. 若是這個ConfigClass是被其餘@Configuration的類導入的(使用@Import),就把這個ConfigClass註冊到Spring容器中;
  2. 每一個@Bean註解的方法,根據@Bean的屬性,而且將這個方法定義爲BeanDefinition註冊到Spring容器中;
  3. 加載@ImportResource定義的配置文件,xml格式或者groovy格式;
  4. 調用每一個ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,由ImportBeanDefinitionRegistrar來實現註冊的邏輯;

總結

Spring中的@Configuration、@ComponentScan、@Import、@ImportResource、@Bean這些註解是ConfigurationClassPostProcessor來負責處理的。ConfigurationClassPostProcessor是BeanFactoryPostProcessor,在Spring註冊完全部的BeanDefinition以後實例化Bean以前進行擴展。ConfigurationClassPostProcessor負責處理這些,其中根據註解的不一樣可能會向Spring註冊新的BeanDefinition。

@Configuration註解描述的類自己會被註冊到容器,@Bean描述的方法會被註冊到容器,@ImportResource描述的配置文件會被加載會解析出BeanDefinition註冊到Spring中

@Import導入的普通類會當作ConfigClass處理,@Import導入的ImportSelector類會執行ImportSelector#selectImports()方法,方法返回的類也做爲導入進行處理@Import的邏輯,@Import導入的ImportBeanDefinitionRegistrar類,會調用它的registerBeanDefinitions()方法,由它來自定義註冊BeanDefinition的邏輯。

相關文章
相關標籤/搜索