SpringBoot -- 自动配置类AopAutoConfiguration解析注册BeanDefinition过程

Posted 做猪呢,最重要的是开森啦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot -- 自动配置类AopAutoConfiguration解析注册BeanDefinition过程相关的知识,希望对你有一定的参考价值。

  在上一篇文章2.5.2.2节介绍了grouping.getImports()会获取所有符合的自动配置类名
·
 然后循环遍历调用processImports来对自动配置类进行处理

 本文以AopAutoConfiguration为栗子,对自动配置类的解析和注册再详细介绍一下

0. AopAutoConfiguration:

  先大概说明下@ConditionOnXXX原理:

  • 如果条件匹配,则条件结果对象ConditionOutcome的内部属性match为true,即该组件允许引入
@Configuration(proxyBeanMethods = false)
// 如果环境(配置文件)没有配置spring.aop.auto键值对,默认引入AopAutoConfiguration(因为 matchIfMissing = true,即上述match为true)
// 如果环境(配置文件)配置了spring.aop.auto键值对,如果value和havingValue相等,则上述match为true,即引入AopAutoConfiguration
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	// 如果加载到Advice.class这个类,则上述match为true,就引入AspectJAutoProxyingConfiguration组件
	@ConditionalOnClass(Advice.class)
	static class AspectJAutoProxyingConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = false)
		// 如果环境(配置文件)没有配置spring.aop.proxy-target-class键值对,默认不引入JdkDynamicAutoProxyConfiguration
		// 因为 matchIfMissing = false,即上述match为false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
				matchIfMissing = false)
		static class JdkDynamicAutoProxyConfiguration {

		}

		@Configuration(proxyBeanMethods = false)
		@EnableAspectJAutoProxy(proxyTargetClass = true)
		// 如果环境(配置文件)没有配置spring.aop.proxy-target-class键值对,默认引入CglibAutoProxyConfiguration
		// 因为 matchIfMissing = true,即上述match为true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		static class CglibAutoProxyConfiguration {

		}
	}

	@Configuration(proxyBeanMethods = false)
	// 如果加载不到org.aspectj.weaver.Advice这个类,则上述match为true,就引入ClassProxyingConfiguration组件
	@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
	@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
			matchIfMissing = true)
	static class ClassProxyingConfiguration {

		ClassProxyingConfiguration(BeanFactory beanFactory) {
			if (beanFactory instanceof BeanDefinitionRegistry) {
				BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
				AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
		}
	}
}


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

 通过上面条件注解的介绍,可以知道,在默认条件,即只引入spring-boot-starter-aop依赖,会引入三个组件Bean:

  • AopAutoConfiguration
  • AspectJAutoProxyingConfiguration
  • CglibAutoProxyConfiguration

1. processGroupImports

  在上一篇文章2.5.2.2节介绍处理自动配置类入口:循环遍历调用processImports来对自动配置类进行处理

 处理AopAutoConfiguration时,processImports入参将配置类名封装成SourceClass
·
 configurationClass为当前主配置类,也就是说通过该主配置类引入的AopAutoConfiguration

public void processGroupImports() {
			... ... ...
			try {
				// 遍历递归处理解析的配置类名取处理配置类,包括嵌套引入的组件配置类;比如@Bean之类
				// 入参将配置类名entry.getImportClassName()封装成ConfigurationClassParser.SourceClass
				// 在内部processConfigurationClass方法入参会将SourceClass转换成ConfigurationClass
				processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
						Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
						exclusionFilter, false);
			}
			... ... ... //异常捕获抛出
		});
	}
}

 1.1. processImports:

 对于AopAutoConfiguration,只有@Configuration、@ConditionalOnProperty注解,直接调用processConfigurationClass去处理

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
		boolean checkForCircularImports) {
	... ... ...

	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			for (SourceClass candidate : importCandidates) {
				if (candidate.isAssignable(ImportSelector.class)) {
					... ... ...
				}
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					... ... ...
				}
				else {
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					// 不属于以上两种类型,当作配置类处理入参将自身转换为ConfigurationClass
					// 会将主配置类configClass添加到ConfigurationClass内部属性this.importedBy
					// 这表明引入的所属关系,即AopAutoConfiguration是通过主配置类引入的
					processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
				}
			}
		}
		... ... ..
}

 1.2. processConfigurationClass:

 先进行@ConditionOnXXX条件匹配,匹配成功,则调用doProcessConfigurationClass解析嵌套类和自身

	protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
		// shouldSkip主要时匹配@ConditionOnXXX的条件,匹配规则见上文,这里不跟源码了
		// 对于AopAutoConfiguration,默认匹配成功引入该组件,则不跳过,即shouldSkip返回false
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}

		... ... ... // 当前AopAutoConfiguration配置类没有重复解析,省略代码

		// 作为配置类,需要去解析是否有嵌套配置类,所以要封装成SourceClass
		SourceClass sourceClass = asSourceClass(configClass, filter);
		do {
			// 核心解析配置类方法,解析嵌套和自身
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);
		// 配置类解析完,添加到configurationClasses,用于上述判断重复解析,以及后续注册
		this.configurationClasses.put(configClass, configClass);
	}

 1.3. doProcessConfigurationClass:

 核心解析配置类方法,对于AopAutoConfiguration,不涉及@ComponentScan、@Import、@Bean等注解
·
 所以此时只调用processMemberClasses进行嵌套类解析

	protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// 内部嵌套类解析
			processMemberClasses(configClass, sourceClass, filter);
		}

		... ... ... // AopAutoConfiguration不涉及 @ComponentScan、@Import等注解,直接省略
		return null;
	}

 1.4. processMemberClasses:

 会获取当前配置类的嵌套类,对于当前AopAutoConfiguration,会获取到:

  • AspectJAutoProxyingConfiguration
  • ClassProxyingConfiguration
	private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
			Predicate<String> filter) throws IOException {

		// 获取内部嵌套类并封装成SourceClass
		Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
		if (!memberClasses.isEmpty()) {
			List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
			for (SourceClass memberClass : memberClasses) {
				// 判断嵌套类是否是配置类且不是自身
				if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
						!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
					candidates.add(memberClass);
				}
			}
			OrderComparator.sort(candidates);
			// 循环解析符合的嵌套类
			for (SourceClass candidate : candidates) {
				if (this.importStack.contains(configClass)) {
					this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
				}
				else {
					this.importStack.push(configClass);
					try {
						// 调用processConfigurationClass核心解析方法来解析						
						// 入参转换成ConfigurationClass,会将配置类configClass添加到ConfigurationClass内部属性this.importedBy
						// 即表明内部嵌套类是通过AopAutoConfiguration引入的
						processConfigurationClass(candidate.asConfigClass(configClass), filter);
					}
					finally {
						this.importStack.pop();
					}
				}
			}
		}
	}

 1.5. 循环递归处理:

 解析嵌套类就和解析AopAutoConfiguration一样,重复调用上述1.2 - 1.4的方法,整体如下:

 递归循环处理后的结果就是会引入以下组件,并封装成ConfigurationClass类型

  • AopAutoConfiguration
  • AspectJAutoProxyingConfiguration
  • CglibAutoProxyConfiguration

 此外,在解析CglibAutoProxyConfiguration时,会引入AspectJAutoProxyRegistrar
·
 在processImports方法会实例化该对象,并添加到CglibAutoProxyConfiguration

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
		boolean checkForCircularImports) {
	... ... ...

	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			for (SourceClass candidate : importCandidates) {
				if (candidate.isAssignable(ImportSelector.class)) {
					... ... ...
				}
				// AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar 条件成立
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
						Class<?> candidateClass = candidate.loadClass();
						// 获取AspectJAutoProxyRegistrar实例对象,但为进行BeanDefinition注册
						ImportBeanDefinitionRegistrar registrar =
								ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
										this.environment, this.resourceLoader, this.registry);
						// 将AspectJAutoProxyRegistrar添加到所属配置类
						// 后面注册BeanDefinition时会调用
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}
				else {
		... ... ...
}

2. loadBeanDefinitionsForConfigurationClass

 在上一篇文章2.6节介绍了注册BeanDefinitions的入口,会循环调用loadBeanDefinitionsForConfigurationClass

private void loadBeanDefinitionsForConfigurationClass(
		ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

	// 条件注解判断是否跳过这个配置类,详情可以参考文末参考链接3
	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			// 如果跳过,Spring容器中移除bean的注册
			this.registry.removeBeanDefinition(beanName);
		}
		// 从importRegistry 中也移除
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}
	// 如果这是一个通过import机制引入的配置类,也就是内部属性importedBy有所属的配置类
	// 比如AopAutoConfiguration是主配置(启动)类引入的,则importedBy存的就是主配置类,上文1.1. 也介绍了
	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		// 现在把配置类里面@Bean注解的方法作为bean定义注册到容器
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}

	// 从配置类导入的bean定义资源中获取bean定义信息并注册到容器,比如xml
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	// 从配置类导入的ImportBeanDefinitionRegistrar中获取bean定义信息并注册到容器	
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

 2.1. registerBeanDefinitionForImportedConfigurationClass

 还是以AopAutoConfiguration为栗子,它是由主配置类引入的,所以条件成立会执行该方法

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
	// 获取当前配置类元数据
	AnnotationMetadata metadata = configClass.getMetadata();
	// 构建BeanDefinition,为AnnotatedGenericBeanDefinition类型
	// 会从元数据metadata获取className,然后给configBeanDef内部属性beanClass赋值
	AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

	ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
	// 设置Scope,默认singleton
	configBeanDef.setScope(scopeMetadata.getScopeName());
	// 解析出beanName,如果是自动配置类,则为beanClass(上文有赋值)
	// 如果是@Component(value = "beanName")这些注解定义有value ,会解析出value值给作beanName
	String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
	// 这是会针对@Lazy、@Primary()、@DependsOn、@Role、@Description注解给当前BeanDefinition进行对应的赋值
	AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

	// 将BeanDefinition封装成持有者对象BeanDefinitionHolder
	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	// 会调用registerBeanDefinition进行BeanDefinition注册,最终会将组件包括嵌套的beanDefinition添加到beanDefinitionMap,以及beanDefinitionNames
	this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
	// 给当前配置类也设置beanName
	configClass.setBeanName(configBeanName);

	if (logger.isTraceEnabled()) {
		logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
	}
}

 最终会将组件包括嵌套的beanDefinition添加到beanDefinitionMap,以及beanDefinitionNames

 2.2. loadBeanDefinitionsForBeanMethod

 这个是处理@Bean的,其实也差不多,这里不展开介绍了
·
 都是构建BeanDefinition,然后根据条件赋值,之后通过registerBeanDefinition注册进去

 2.3. loadBeanDefinitionsFromRegistrars

 @EnableAspectJAutoProxy注解会引入AspectJAutoProxyRegistrar,在上文1.5也提到
·
 AspectJAutoProxyRegistrar重写了registerBeanDefinitions方法,这里便是调用重写后的方法注册

public void registerBeanDefinitions(
		AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

	// 默认先创建一个AnnotationAwareAspectJAutoProxyCreator的BeanDefinition,然后注册进去
	// 注册的beanName为org.springframework.aop.config.internalAutoProxyCreator
	AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

	// 获取@EnableAspectJAutoProxy的键值对以上是关于SpringBoot -- 自动配置类AopAutoConfiguration解析注册BeanDefinition过程的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot自动配置原理分析

Spring boot运行原理-自定义自动配置类

Springboot之加载自动配置类

springboot属性类自动加载配置文件中的值

SpringBoot05:自动配置原理

SpringBoot 动态过滤自动配置类