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的逻辑。

相关文章
相关标签/搜索