spring之ImportSelector和DeferredImportSelector注解分析

spring 专栏收录该内容
53 篇文章 0 订阅

写在前面

本文基于springboot来进行测试,对springboot不了解或者是想要直接获取测试代码的朋友,可以参考这篇文章

1:源码分析

1.1:入口分析

当我们执行main方法,然后调用run函数,最终会执行到代码org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>),该方法是用来解析java config类的,即标记有@Configuration注解的类,的我们来通过调用栈看下:
在这里插入图片描述
其中4执行BeanFactoryPostProcessor的时候会调用到该方法,关于该过程详细可以参考这篇文章parse方法也正是我们要分析的入口了。

1.2:parse

源码位置:org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>),源码如下:

org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
	for (BeanDefinitionHolder holder : configCandidates) {
		// 获取bean定义
		BeanDefinition bd = holder.getBeanDefinition();
		try {
			// <202105201137>
			if (bd instanceof AnnotatedBeanDefinition) {
				// <202105231114>
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}
			....snip...
	}
	// <202105201138>
	this.deferredImportSelectorHandler.process();
}

<202105201137>处如果是AnnotatedBeanDefinition类型,这是BeanDefinition的子接口,对外暴露了注解源信息,源码如下:

org.springframework.beans.factory.annotation.AnnotatedBeanDefinition
// 扩展BeanDefinition接口,在不需要加载class类的前提下,暴漏关于bean对应class的AnnotationMetadata
public interface AnnotatedBeanDefinition extends BeanDefinition {

	// 返回bean定义对应的注解元信息对象
	AnnotationMetadata getMetadata();

	// 如果是有工厂方法的话,返回工厂方法的注解元信息对象
	@Nullable
	MethodMetadata getFactoryMethodMetadata();

}

<202105231114>处源码如下:

org.springframework.context.annotation.ConfigurationClassParser#parse(org.springframework.core.type.AnnotationMetadata, java.lang.String)
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
	// <202105231117>
	processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

<202105231117>处源码如下:

org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
	...snip...
	// 这里通过do..while循环来依次处理每个bean的相关注解
	do {
		// <202105231122>
		sourceClass = doProcessConfigurationClass(configClass, sourceClass);
	} while (sourceClass != null);
	...snip...
}

<202105231122>(重点!!!)处源码如下:

org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {
	// 如果是标注了@Component注解
	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		processMemberClasses(configClass, sourceClass);
	}

	// 处理@PropertySource注解
	// 关于PropertySource可以参考:https://blog.csdn.net/wang0907/article/details/117037183
	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注解
	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的信息,扫描对应的类生成bean定义
			// 最终成为spring bean
			Set<BeanDefinitionHolder> scannedBeanDefinitions =
					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
			// 递归处理有@Configuration注解的bean定义
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
				if (bdCand == null) {
					bdCand = holder.getBeanDefinition();
				}
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					parse(bdCand.getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}
	// <202105251519>
	// 处理@Import注解
	// 关于@Import注解可以参考:https://blog.csdn.net/wang0907/article/details/113131549#t7
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	// 处理@ImportResource注解
	// 关于@ImportResource注解,可以参考:https://blog.csdn.net/wang0907/article/details/117037183
	// 一般在启动类上我们不会使用@ImportResource注解,所以这里一般为null
	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);
		}
	}

	// <202105251442>
	// 处理类中方法的@Bean注解
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}

	processInterfaces(configClass, sourceClass);

	// 递归处理@Configuration注解所在类的父类,返回值会在外层的do...while循环中被处理
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (superclass != null && !superclass.startsWith("java") &&
				!this.knownSuperclasses.containsKey(superclass)) {
			this.knownSuperclasses.put(superclass, configClass);
			return sourceClass.getSuperClass();
		}
	}

	// 如果是执行到这里,则外层的do...while循环也就结束了
	return null;
}

<202105251442>处是处理标记有@Bean注解的方法,比如在启动类配置如下:

@SpringBootApplication
public class HelloWorldMainApplication {

    public static void main(String[] args) throws URISyntaxException, IOException {
        ConfigurableApplicationContext run = SpringApplication.run(HelloWorldMainApplication.class, args);
        MyPropertySourceBean bean = run.getBean(MyPropertySourceBean.class);
        System.out.println(bean);
    }

    @Bean
    public Object obj1() {
        return new Object();
    }

    @Bean
    public Object obj2() {
        return new Object();
    }
}

则此处运行结果就如下图,获取的是obj1obj2这两个方法的信息:
在这里插入图片描述
<202105251519>处会处理ImportSelector,具体参考1.3:processImports<202105201138>处用于处理DeferredImportSelector,具体参考1.4:deferredImportSelectorHandler_process

1.3:processImports

源码:

org.springframework.context.annotation.ConfigurationClassParser#processImports
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
	// <202105251535>
	if (importCandidates.isEmpty()) {
		return;
	}
	// 循环引入等异常情况检测
	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			// 循环处理每个候选者
			for (SourceClass candidate : importCandidates) {
				// 如果是ImportSelector
				if (candidate.isAssignable(ImportSelector.class)) {
					Class<?> candidateClass = candidate.loadClass();
					// 创建ImportSelector类实例
					ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
					// 回调相关aware
					ParserStrategyUtils.invokeAwareMethods(
							selector, this.environment, this.resourceLoader, this.registry);
					// 如果是也实现了DeferredImportSelector,注意这里就不执行else了,即不执行ImportSelector的selectImports方法
					if (selector instanceof DeferredImportSelector) {
						// <202105251610>
						// 处理DeferedImportSelector逻辑
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}
					else {
						// 通过执行ImportSelector接口的selectImports方法,获取要import的类名称数组,重要!!!
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
						processImports(configClass, currentSourceClass, importSourceClasses, false);
					}
				}
				// 如果是ImportBeanDefinitionRegistrar,用于注册bean定义
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					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 {
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					processConfigurationClass(candidate.asConfigClass(configClass));
				}
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to process import candidates for configuration class [" +
					configClass.getMetadata().getClassName() + "]", ex);
		}
		finally {
			this.importStack.pop();
		}
	}
}

<202105251535>处的值来源于@Import注解信息的值,来源如下图:
标记1和2的就是结果
标记1和2的就是结果
最终结果值如下图:
在这里插入图片描述
<202105251610>处理在实现了ImportSelector接口时,也实现了DeferredImportSelector接口的情况,源码如下:

org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
	DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
	if (this.deferredImportSelectors == null) {
		DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
		handler.register(holder);
		// 执行相关的import的工作
		handler.processGroupImports();
	}
	else {
		// 添加到集合中,统一处理,详细参考“1.4:deferredImportSelectorHandler_process”
		this.deferredImportSelectors.add(holder);
	}
}

1.4:deferredImportSelectorHandler_process

重要!!!,用来引入springboot在META-INF/spring.factories文件中配置的自动配置类。
源码如下:

org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process
public void process() {
			// <202105251650>
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
	this.deferredImportSelectors = null;
	try {
			if (deferredImports != null) {
			DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
			deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
			deferredImports.forEach(handler::register);
			// <202105251708>
			handler.processGroupImports();
		}
	}
	finally {
		this.deferredImportSelectors = new ArrayList<>();
	}
}

<202105251650>默认只有一个即AutoConfigurationImportSelector类,如下图:
在这里插入图片描述
<202105251708>处是执行具体的引入,源码如下:

org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
public void processGroupImports() {
	// 循环所有,依次处理
	for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
		
		// grouping.getImports()处就是获取所有的自动配置类,然后在依次递归处理import信息
		grouping.getImports().forEach(entry -> {
			ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
			try {
				processImports(configurationClass, asSourceClass(configurationClass),
						asSourceClasses(entry.getImportClassName()), false);
			}
			catch (BeanDefinitionStoreException ex) {
				throw ex;
			}
			catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to process import candidates for configuration class [" +
								configurationClass.getMetadata().getClassName() + "]", ex);
			}
		});
	}
}

最后

参考文章列表:
ImportSelector与DeferredImportSelector的区别(spring4)

  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值