Spring Configuration注解

Spring @Configuration标识的class,能够作为Spring的配置类,用来构建Bean,引入资源文件等等功能。所以有必要理解一下它是如何工作的。

注册 ConfigurationClassPostProcessor Bean

此注解的核心处理逻辑在ConfigurationClassPostProcessor类中,此类实现了BeanDefinitionRegistryPostProcessor接口,所以此类也必须被注册成BeanConfigurationClassPostProcessor注册的逻辑在AnnotationConfigUtils.registerAnnotationConfigProcessors()方法内

...
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
		"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
...
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
	RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
	def.setSource(source);
	beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
...

上面的注册ConfigurationClassPostProcessor会在AnnotatedBeanDefinitionReader构造时调用。

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
	...
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

AnnotatedBeanDefinitionReader类是用来处理注解方式注册bean的处理器。此类会在AnnotationConfigEmbeddedWebApplicationContext或者AnnotationConfigApplicationContext构造时构建。对于前2contextSpringBoot会在启动时,会根据类路径下是否有web相关的类,决定启用哪个context

到这里为止,ConfigurationClassPostProcessor注册为bean的流程已经显而易见了。

ConfigurationClassPostProcessor 处理 @Configuration 注解

由于ConfigurationClassPostProcessor继承了BeanDefinitionRegistryPostProcessor接口,直接看它实现的postProcessBeanDefinitionRegistry方法,发现它的核心处理逻辑在processConfigBeanDefinitions方法内。

我们将它的内部流程分为几块来看。

判断是否有 @Configuration 注解标注

String[] candidateNames = registry.getBeanDefinitionNames();

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));
	}
}

// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
	return;
}

上面这段代码里,首先isFullConfigurationClassisLiteConfigurationClass方法判断此类是否已经经过处理,如果不是,调用ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory),判断此类是否标有@Configuration注解,如果有的话放入configCandidates;

checkConfigurationClassCandidate方法

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
	String className = beanDef.getBeanClassName();
	if (className == null || beanDef.getFactoryMethodName() != null) {
		return false;
	}

	AnnotationMetadata metadata;
	...
	if (isFullConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	else if (isLiteConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
		return false;
	}
	// It's a full or lite configuration candidate... Let's determine the order value, if any.
	Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
	if (orderAttributes != null) {
		beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE));
	}
	return true;
}

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
	return metadata.isAnnotated(Configuration.class.getName());
}

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
...
	// Any of the typical annotations found?
	for (String indicator : candidateIndicators) {
		if (metadata.isAnnotated(indicator)) {
			return true;
		}
	}
	// Finally, let's look for @Bean methods...
	try {
		return metadata.hasAnnotatedMethods(Bean.class.getName());
	}
	...
}
  1. isFullConfigurationCandidate判断是否标有Configuration注解;
  2. isLiteConfigurationCandidate判断是否有ComponentComponentScanImportImportResourceBean注解,但是没有Configuration注解。
  3. checkConfigurationClassCandidate方法内,会根据不同的配置类类型,对BeanDefinition设置不同的属性,full or lite

处理标有 Configuration 注解的类

ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法里剩下的代码

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
	parser.parse(candidates);
	parser.validate();

	Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(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<String>(Arrays.asList(candidateNames));
		Set<String> alreadyParsedClasses = new HashSet<String>();
		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());

此处可以看到parser调用了parse()方法,此方法内会解析配置类.内部比较核心的方法是doProcessConfigurationClass

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {
	...
	// Process any @PropertySource annotations
	// 处理 @PropertySources
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
		if (this.environment instanceof ConfigurableEnvironment) {
			processPropertySource(propertySource);
		}
		else {
			logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
					"]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}

	// Process any @ComponentScan annotations
	// 处理 @ComponentScan
	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
	if (!componentScans.isEmpty() &&
			!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
		for (AnnotationAttributes componentScan : componentScans) {
			// The config class is annotated with @ComponentScan -> perform the scan immediately
			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 needed
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(
						holder.getBeanDefinition(), this.metadataReaderFactory)) {
					parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

	// Process any @Import annotations
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	// Process any @ImportResource annotations
	if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		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);
		}
	}

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

从上面的代码可以看到此方法内处理了各种常用的注解。具体就不在此文里深入,对于其它注解之后会在专门的文章里描述。

回到parser.parse调用之后的逻辑,Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());调用parser.getConfigurationClasses()获取到配置类需要注册为bean的所有类。

if (this.reader == null) {
	this.reader = new ConfigurationClassBeanDefinitionReader(
			registry, this.sourceExtractor, this.resourceLoader, this.environment,
			this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);

上面这段代码会将获取到的所有类,通过ConfigurationClassBeanDefinitionReader.loadBeanDefinitions方法,注册为bean;

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 (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}

	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

到这里为止,@Configuration配置类解析,注册相关的bean的流程已经比较清晰了。具体细节还是不再深入。

总结

@Configuration实现过程主要基于BeanDefinitionRegistryPostProcessor接口,解析注解、处理资源等,最后动态注册bean来实现。不得不说Spring内部的几个XxxPostProcessor对于扩展非常方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值