栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

@ComponentScan注解如何生效

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

@ComponentScan注解如何生效

约定

由于Spring类名普遍很长,为了方便阅读,类名第一次出现时会以全限定名称出现,即包名+类名的形式,第二次开始只会写类名;方法名类似,第一次出现时,会以类名+方法名的形式,第二次开始只会写方法名(同名方法例外,会一直使用类名+方法名形式)

前提了解

org.springframework.context.annotation.AnnotationConfigUtils这个类注册了很多Spring用到的PostProcessor组件,其中包括org.springframework.context.annotation.ConfigurationClassPostProcessor

ConfigurationClassPostProcessor说明

ConfigurationClassPostProcessor顾名思义就是用来处理Configuration类的后置处理器,既然是后置处理器,那按照Spring使用方式,那必定也是在bean初始化过程中回调

通过查看其继承体系,可以看到其实现了org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor接口,而BeanDefinitionRegistryPostProcessor又继承了org.springframework.beans.factory.config.BeanFactoryPostProcessor
大致结构如下:
ConfigurationClassPostProcessor -implement-> BeanDefinitionRegistryPostProcessor -extends-> BeanFactoryPostProcessor

BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor这两个接口,每个接口都只定义了一个方法,分别是:

  • BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
  • BeanFactoryPostProcessor#postProcessBeanFactory

而在Spring Bean初始化过程中,postProcessBeanDefinitionRegistry回调早于postProcessBeanFactory,所以接下来我们先分析postProcessBeanDefinitionRegistry

流程分析 1. ConfigurationClassPostProcessor
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	// 省略部分代码
	processConfigBeanDefinitions(registry);
}

这里postProcessBeanDefinitionRegistry是调用了processConfigBeanDefinitions方法

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	// 省略部分代码
	// Parse each @Configuration class
	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);

	Set candidates = new linkedHashSet<>(configCandidates);
	do {
		// 这里只关注主要逻辑,即Configuration类的解析过程,所以省略了部分代码,省略的代码中申明了Set变量candidates ,里面元素都是标注了@Configuration注解的类
		parser.parse(candidates);
		// snipped
	}
}

processConfigBeanDefinitions方法新建了ConfigurationClassParser对象,然后调用了org.springframework.context.annotation.ConfigurationClassParser#parse方法,入参是标注了@Configuration注解的类

public void parse(Set configCandidates) {
	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		// 省略部分代码
		if (bd instanceof AnnotatedBeanDefinition) {
			parse(((AnnotatedBeanDefinition) bd).getmetadata(), holder.getBeanName());
		}
		// 省略部分代码
	}

	this.deferredimportSelectorHandler.process();
}
2. ConfigurationClassParser

可以看到parse方法调用了ConfigurationClassParser#parse重载方法

protected final void parse(@Nullable String className, String beanName) throws IOException {
	Assert.notNull(className, "No bean class name for configuration class bean definition");
	metadataReader reader = this.metadataReaderFactory.getmetadataReader(className);
	processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}

protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter) throws IOException {
	// 省略部分代码
	do {
		sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
	}
	while (sourceClass != null);
}

protected final SourceClass doProcessConfigurationClass(
	ConfigurationClass configClass, SourceClass sourceClass, Predicate filter)
		throws IOException {
	// 省略部分代码
	// Process any @ComponentScan annotations
	// 获取类上@ComponentScan注解的元信息
	Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getmetadata(), ComponentScans.class, ComponentScan.class);
	// 过滤标注了@ComponentScan注解的类
	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
			// 解析@ComponentScan注解
			Set scannedBeanDefinitions =
					this.componentScanParser.parse(componentScan, sourceClass.getmetadata().getClassName());
			// 省略部分代码
		}
	}
	// 省略部分代码
	return null;
}

重载方法调用了processConfigurationClass,processConfigurationClass又调用了ConfigurationClassParser#doProcessConfigurationClass,
doProcessConfigurationClass先是获取类上的@ComponentScan注解信息,如果有,则会调用
org.springframework.context.annotation.ComponentScanAnnotationParser#parse方法进行解析,而ComponentScanAnnotationParser#parse正是解析@ComponentScan注解所在。

那ComponentScanAnnotationParser对象是怎么来的呢?答案是:在创建ConfigurationClassParser对象时创建的,而且是直接通过new的方式写死的,并没有提供set方法

public ConfigurationClassParser(metadataReaderFactory metadataReaderFactory,
	ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
		BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
	// 省略部分代码
	this.componentScanParser = new ComponentScanAnnotationParser(
			environment, resourceLoader, componentScanBeanNameGenerator, registry);
}
小结

通过以上分析应该就知道了@ComponentScan注解是如何与ComponentScanAnnotationParser解析类对应起来的了,答案是:通过在ConfigurationClassParser#doProcessConfigurationClass取出出类上@ComponentScan注解的元信息,如果@ComponentScan注解元信息存在,就交给ComponentScanAnnotationParser#parse去解析。
而ComponentScanAnnotationParser是在ConfigurationClassParser构造器里直接new 出来的。这样@ComponentScan就和ComponentScanAnnotationParser联系起来了。
而ConfigurationClassParser则是在Spring Bean初始化时回调BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry阶段创建

Debug分析

以上从代码层面分析了执行流程,但是直接阅读代码很容易绕晕,涉及到多态的情况下可能还会看错类。这种情况下可以借助IDE来更高效清晰的查看调用栈,比如针对以上流程,我们先是知道了ComponentScanAnnotationParser是@ComponentScan注解的解析器,所以可以在ComponentScanAnnotationParser#parse方法打断点,然后在IDE里看调用栈

可以很清晰的看到方法调用链,这样就可以一层一层倒推了。
比如这里ComponentScanAnnotationParser#parse方法是在ConfigurationClassParser#doProcessConfigurationClass调用的,并且componentScanParser是ConfigurationClassParser的一个成员变量,那我们就可以查看componentScanParser是在哪创建的

通过搜索可以看到componentScanParser是在ConfigurationClassParser构造器里创建的,所以也就明白@ComponentScan注解是如何和ComponentScanAnnotationParser解析类对应起来的了,就是写死的。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/356414.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号