08 ConfigurationClassPostProcessor源码解析

由于md文档部分hugo插件语法不兼容,建议访问作者网站查阅文章:wlizhi.cc

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


1 主流程

在调用 invokeBeanDefinitionRegistryPostProcessors() 是,会调用到这个实现类中,来到源码:
{{< highlight java “linenos=table,hl_lines=12 25 36,linenostart=1” >}}
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List configCandidates = new ArrayList<>();
// 获取到所有的BeanDefinitionNames
String[] candidateNames = registry.getBeanDefinitionNames();
// 循环调用
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// 省略无关代码…
//检查给定的bean定义是否适合配置类(或在配置/组件类中声明的嵌套组件类,也要自动注册),并进行相应标记。
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 省略无关代码…
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set candidates = new LinkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
// TODO 重要程度:5 ConfigurationClassParser的parse方法调用,这里解析了一系列的注解
parser.parse(candidates);
parser.validate();
Set 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());
}
// 这里装载了BeanDefinition
this.reader.loadBeanDefinitions(configClasses);
// 省略无关代码…
}
while (!candidates.isEmpty());
// 省略无关代码…
}
}
{{< /highlight >}}
划重点(上面源码中的高亮部分):

  1. 校验是否符合条件,完成对@Configuration、@Component、@ComponentScan、@Import、@ImportResource、方法上包含@Bean 的支持。
  2. 执行 parse(),完成解析。
  3. loadBeanDefinitions(),装载 BeanDefinition。

2 循环所有benaName、校验bean是否符合


进入 checkConfigurationClassCandidate() 源码:

abstract class ConfigurationClassUtils {
    public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
        // 省略无关代码...
		// 类上是否包含@Configuration
		if (isFullConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		// 类上是包含@Component、@ComponentScan、@Import、@ImportResource。方法上包含@Bean
		else if (isLiteConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}
        // 省略无关代码
		return true;
	}
}

isFullConfigurationCandidate() 源码:

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

isLiteConfigurationCandidate() 源码:

abstract class ConfigurationClassUtils {
    // 注意这个成员变量,包含了对一些内置注解的支持。
   	private static final Set<String> candidateIndicators = new HashSet<>(8);
   
   	static {
   		// 添加了一系列注解的支持。
   		candidateIndicators.add(Component.class.getName());
   		candidateIndicators.add(ComponentScan.class.getName());
   		candidateIndicators.add(Import.class.getName());
   		candidateIndicators.add(ImportResource.class.getName());
   	}
    public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
		// 类上是包含@Component、@ComponentScan、@Import、@ImportResource。方法上包含@Bean
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}
        // @Bean的支持
        return metadata.hasAnnotatedMethods(Bean.class.getName());
	}
}

3 解析

来到 parse() 源码:

class ConfigurationClassParser {
	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			//这是哪个分支的解析会走到同一个方法里
            if (bd instanceof AnnotatedBeanDefinition) {
                //执行解析
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                //执行解析
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                // TODO 执行解析,记着这个方法,这里在满足条件时会被递归调用。
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
		}
		this.deferredImportSelectorHandler.process();
	}
}

对不同类型的 BeanDefinition 会调用不同的重载 parse(),实际上,最终都会进到这个方法:

class ConfigurationClassParser {
	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		// 省略无关代码...
		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			// 执行解析
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);

		// 这里将配置类的信息放入缓存中,等解析完之后,会将配置类进行LoadBeanDefinitions();
		this.configurationClasses.put(configClass, configClass);
	}
}

是在一个循环中调用的,如果返回不为空,就一直执行。每次会更新sourceClass的值。

进入 doProcessConfigurationClass():
{{< highlight java “linenos=table,hl_lines=23-46,linenostart=1” >}}
class ConfigurationClassParser {
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
// Component注解支持
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
}

	// Process any @PropertySource annotations
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
		if (this.environment instanceof ConfigurableEnvironment) {
			processPropertySource(propertySource);
		}
		else {
			logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
					"]. Reason: Environment must implement ConfigurableEnvironment");
		}
	}

	// @ComponentScan,@ComponentScans支持
	// Process any @ComponentScan annotations
	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) {
			// 使用@ComponentScan注释配置类->立即执行扫描
			// 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) {
				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
				if (bdCand == null) {
					bdCand = holder.getBeanDefinition();
				}
				// TODO 如果是配置类,则递归调用ConfigurationClassParser.parse()
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					parse(bdCand.getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

	// 处理@Import注解导入
	// Process any @Import annotations
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	// 处理@ImportResource注解导入
	// Process any @ImportResource annotations
	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);
		}
	}

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

	// 处理接口上的默认方法
	// Process default methods on interfaces
	processInterfaces(configClass, sourceClass);

	// Process superclass, if any
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (superclass != null && !superclass.startsWith("java") &&
				!this.knownSuperclasses.containsKey(superclass)) {
			this.knownSuperclasses.put(superclass, configClass);
			// Superclass found, return its annotation metadata and recurse
			return sourceClass.getSuperClass();
		}
	}

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

}
{{< /highlight >}}

以上代码完成了对相关注解的扫描解析。这里着重看一下 ComponentScan 的解析,其他的注解解析,源码中有注释。

ComponentScan的解析,大概分为这么几步:

  1. 将扫描到的类封装成 BeanDefinitionHolder,放入一个 Set 集合中。(在扫描方法里已经完成了 BeanDefinition 的注册)
  2. 调用 checkConfigurationClassCandidate(),这个方法具体怎么检查的,前面有解释
  3. 如果 check 的结果为true,说明也是个配置类,递归调用 parse() 方法。

关于 componentScanParser.parse() 的源码分析,在 Spring标签解析-BeanDefinitionParser 中有说明。

来到 processImports(),看一下是如何处理@Import注解的。首先来到getImports():

class ConfigurationClassParser {
	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<>();
		Set<SourceClass> visited = new LinkedHashSet<>();
		// 收集@import注解。
		collectImports(sourceClass, imports, visited);
		return imports;
	}
}

继续往下跟踪代码:

class ConfigurationClassParser {
	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {
		// 获取所有的注解,遍历判断注解是否是@Import,将其搜集起来
		if (visited.add(sourceClass)) {
			for (SourceClass annotation : sourceClass.getAnnotations()) {
				String annName = annotation.getMetadata().getClassName();
				if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
					collectImports(annotation, imports, visited);
				}
			}
			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
	}
}

从上面源码中可以看到,这里获取了所有的注解,然后遍历寻找@Import注解,将其搜集起来。

然后进入 processImports(),会将上面流程中搜集到的@Import注解,作为参数传递进来:

class ConfigurationClassParser {
	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

		if (importCandidates.isEmpty()) {
			return;
		}
		// 检测循环导入
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			// 这里是一个ArrayDeque
			this.importStack.push(configClass);
			
            for (SourceClass candidate : importCandidates) {
                // 这里会处理ImportSelector类型的。
                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 {
                        // 实例化 ImportSelector 后,会执行selectImports()方法。获取到一个className数组。
                        // 将其封装为SourceClass,然后递归调用当前方法,最终会走到else语句块中。
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                // EnableAspectJAutoProxy中导入的AspectJAutoProxyRegistrar就是实现了ImportBeanDefinitionRegistrar
                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));
                }
            }
		}
			// 省略...
	}
}

上面代码分为三个分支:

  • 遍历所有的importCandidates,判断是否为ImportSelector,如果为true,则执行selectImports(),获取到导入的className数组。然后递归调用,最终会执行到步骤3。
  • 处理ImportBeanDefinitionRegistrar类型的,与步骤1类似,然后直接注册。跳出方法
  • 将 Metadata 存储到 importStack 中,然后递归调用 processConfigurationClass,这也是一个@Configuration类。

4 装载BeanDefinition

loadBeanDefinitions() 方法参数是一个 ConfigurationClass 的集合,从名字就可以看出,这是一个配置类的集合。

这些配置类是在上面的流程中,扫描包中的类时,扫描到有配置信息的类。

最终会进行以下调用,又会回到 springioc 容器初始化时,解析标签的那段代码。

关键源码:

class ConfigurationClassBeanDefinitionReader {
	private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
        // 省略无关代码...
		// @Import注解中,导入的类,包装为BeanDefinition并注册到容器。
        if (configClass.isImported()) {
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
        // @Bean注解的方法,将对应的metadata包装为BeanDefinition并注册到容器。
        // 这里并没有执行方法,真正执行方法是在实例化的时候。
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            loadBeanDefinitionsForBeanMethod(beanMethod);
        }

        // 加载导入资源
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        // 这里是加载ImportBeanDefinitionRegistrar接口注册的BeanDefinition,通过调用registerBeanDefinitions()来注入
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}
}

从源码注释中可以很清楚的看到,loadBeanDefinitions() 都做了那些事情。

  1. 解析@Import 导入的类,包装为 BeanDefinition 并注册到容器。
  2. 解析 @Bean 注解的方法,包装成 BeanDefinition 并注册到容器。
  3. 加载导入资源的BeanDefinition,这个是import标签的支持。
  4. 调用容器中已注册的 ImportBeanDefinitionRegistrar 实现类的 registerBeanDefinitions()。

{{< admonition type=“tip” title=“技巧” open=true >}}
AOP的支持,就是通过 ImportBeanDefinitionRegistrar.registerBeanDefinitions() 注册进来的,其实现类是 AspectJAutoProxyRegistrar。
{{< /admonition >}}

调用链流程如下:

{{< mermaid >}}
graph TB;
A(loadBeanDefinitions) --> |forEach| B(configClass)
B --> B2(loadBeanDefinitionsForConfigurationClass)
B2 --> C{分别调用}
C -->|One| D(loadBeanDefinitionsFromImportedResources)
C -->|Two| E(loadBeanDefinitionsFromRegistrars)
D --> D-1(loadBeanDefinitions)
D-1 --> D-2(doLoadBeanDefinitions)
D-2 --> D-3(registerBeanDefinitions)
D-3 --> D-4(doRegisterBeanDefinitions)
D-4 --> D-5(parseBeanDefinitions)
D-4 --> D-5-1(parseDefaultElement)
E --> E-1(registerBeanDefinitions)
{{< /mermaid >}}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值