@Import注解源码解析

34 篇文章 1 订阅
31 篇文章 1 订阅

一、简介

本文将对 Spring 如何处理 @Import 注解的源码进行解析

二、@Import注解的几种用法

在@Import注解的参数里可以填写一个类的数组,而源码上已经告诉我们常用的几种导入类型

image-20230703111310107

  • 导入的类是一个标注了 @Configuration 的配置类:Spring容器实例化这个配置类
  • 导入的类是 ImportSelector 接口的实现类:如果导入的类是 ImportSelector 接口的实现类,实例化这个类之后,会执行其 selectImports 方法,如果实现的是 ImportSelector 接口的子类 DeferredImportSelector,会执行其 selectImports 方法,但是时机会延后,这个后面具体讲
  • 导入的类是 ImportBeanDefinitionRegistrar 接口的实现类:如果导入的类是 ImportBeanDefinitionRegistrar 接口的实现类,实例化这个类之后,会执行其 registerBeanDefinitions 方法

三、@Import注解源码解析

1、ConfigurationClassPostProcessor

下面通过源码来看 Spring 是怎么处理 @Import 注解的,关于@Import注解的处理过程,在 ConfigurationClassPostProcessor 这个类里,这个类是用来处理 @Configuration 注解相关逻辑的,它实现了 BeanDefinitionRegistryPostProcessor 接口,而 BeanDefinitionRegistryPostProcessor 接口又是 BeanFactoryPostProcessor 的子接口,熟悉 Spring 源码的同学会比较熟悉这两个类,这是Spring的后置处理器

  • BeanFactoryPostProcessor:用于修改Bean定义(BeanDefinition
  • BeanDefinitionRegistryPostProcessor:用来注册 BeanDefinition 到Spring容器里

具体这里就不展开了,想了解的可以去看看 Spring 的核心方法 refresh 方法里的 invokeBeanFactoryPostProcessors(beanFactory);,这里只需要知道,在 Spring 容器启动过程中,会调用 ConfigurationClassPostProcessor 类的 postProcessBeanDefinitionRegistry 方法,下面我们从这个方法的入口开始看

2、ConfigurationClassPostProcessor 类的 postProcessBeanDefinitionRegistry 方法

image-20230703135037237

前面那一段,是一个防重复处理的逻辑,不用关心,重点是 processConfigBeanDefinitions 方法,跟进去看下

//ConfigurationClassPostProcessor
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    //1、找到所有的配置类
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	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
    //2、如果没找到配置类就直接返回
	if (configCandidates.isEmpty()) {
		return;
	}

	// Sort by previously determined @Order value, if applicable
    //3、为找到的配置类排序
	configCandidates.sort((bd1, bd2) -> {
		int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
		int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
		return Integer.compare(i1, i2);
	});

	// Detect any custom bean name generation strategy supplied through the enclosing application context
    //4、如果registry是SingletonBeanRegistry类型,且包含这个CONFIGURATION_BEAN_NAME_GENERATOR实例,就设置两个属性
	SingletonBeanRegistry sbr = null;
	if (registry instanceof SingletonBeanRegistry) {
		sbr = (SingletonBeanRegistry) registry;
		if (!this.localBeanNameGeneratorSet) {
			BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
			if (generator != null) {
				this.componentScanBeanNameGenerator = generator;
				this.importBeanNameGenerator = generator;
			}
		}
	}

	if (this.environment == null) {
		this.environment = new StandardEnvironment();
	}

	// Parse each @Configuration class
    //5、实例化ConfigurationClassParser对象
	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);

	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
	Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
	do {
        //6、处理配置类
		parser.parse(candidates);
        //7、验证配置类的合法性和正确性
		parser.validate();

		Set<ConfigurationClass> 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());
		}
        //加载Bean定义到Spring容器里
		this.reader.loadBeanDefinitions(configClasses);
		alreadyParsed.addAll(configClasses);

		candidates.clear();
		if (registry.getBeanDefinitionCount() > candidateNames.length) {
			String[] newCandidateNames = registry.getBeanDefinitionNames();
			Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
			Set<String> alreadyParsedClasses = new HashSet<>();
			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());

	// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
	if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
		sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
	}

	if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
		// Clear cache in externally provided MetadataReaderFactory; this is a no-op
		// for a shared cache since it'll be cleared by the ApplicationContext.
		((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
	}
}

这个方法可以简单的概括成以下几个步骤:

  1. 找到所有的配置类;
  2. 循环解析每个配置类;
  3. 把配置类加载到 Spring 容器里;

最重要的就是解析配置类,ConfigurationClassParser 类的 parse 方法,我们跟进去看下

3、ConfigurationClassParser 类的 parse 方法

//ConfigurationClassParser
public void parse(Set<BeanDefinitionHolder> configCandidates) {
	for (BeanDefinitionHolder holder : configCandidates) {
		BeanDefinition bd = holder.getBeanDefinition();
		try {
			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 {
				parse(bd.getBeanClassName(), holder.getBeanName());
			}
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(
					"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
		}
	}

	this.deferredImportSelectorHandler.process();
}

不论是哪个 if 条件,最终都会走到 ConfigurationClassParser 类的 processConfigurationClass 方法里,而且这些方法会将 DeferredImportSelector 接口的实现类放到 deferredImportSelectorHandler 里,在这个方法最后,调用这些类

image-20230703151637688

这里先跳过,我们先看 ConfigurationClassParser 类的 processConfigurationClass 方法

//ConfigurationClassParser
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
	if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
		return;
	}

	ConfigurationClass existingClass = this.configurationClasses.get(configClass);
	if (existingClass != null) {
		if (configClass.isImported()) {
			if (existingClass.isImported()) {
				existingClass.mergeImportedBy(configClass);
			}
			// Otherwise ignore new imported config class; existing non-imported class overrides it.
			return;
		}
		else {
			// Explicit bean definition found, probably replacing an import.
			// Let's remove the old one and go with the new one.
			this.configurationClasses.remove(configClass);
			this.knownSuperclasses.values().removeIf(configClass::equals);
		}
	}

	// Recursively process the configuration class and its superclass hierarchy.
	SourceClass sourceClass = asSourceClass(configClass);
	do {
		sourceClass = doProcessConfigurationClass(configClass, sourceClass);
	}
	while (sourceClass != null);

	this.configurationClasses.put(configClass, configClass);
}

重点是 doProcessConfigurationClass 方法,跟进

//ConfigurationClassParser
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {

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

	// 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) {
			// 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();
				}
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
					parse(bdCand.getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

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

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

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

这个方法也是很长的一段,关于 @Import 注解的其实就在 processImports 方法里,我们跟进看下

//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 {
		this.importStack.push(configClass);
		try {
			for (SourceClass candidate : importCandidates) {
				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 {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
						processImports(configClass, currentSourceClass, importSourceClasses, false);
					}
				}
				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));
				}
			}
		}
		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();
		}
	}
}

可以看到核心代码就是对 @Import 注解的参数,那个 Class 数组做循环处理,而 if 条件是按我们之前所说的三种情况做区分的

image-20230703151309460

@Configuration 注解类型的就不讲了,就是再回去调用处理 @Configuration 注解的方法,下面重点看下 ImportBeanDefinitionRegistrar 接口和 ImportSelector 接口的场景

4、处理 ImportBeanDefinitionRegistrar 接口实现类

image-20230703154651578

先是实例化这个类,如果这个类实现了 Aware 相关的接口,就去设置下相关的属性,最后调用 addImportBeanDefinitionRegistrar 方法,将对象放到 importBeanDefinitionRegistrars 这个 map 里

image-20230703162607872

那么这个类的 registerBeanDefinitions 方法究竟在什么时候调用的呢?

上面我们讲到在解析完配置类之后,会调用 this.reader.loadBeanDefinitions(configClasses); 方法,我们跟进这个方法

image-20230703162836338

继续跟进 loadBeanDefinitionsForConfigurationClass 方法

image-20230703162909334

configClass.getImportBeanDefinitionRegistrars() 方法就是获取的上面所说的 importBeanDefinitionRegistrars 这个map,继续跟进 loadBeanDefinitionsFromRegistrars 方法

image-20230703162945514

可以看到循环map,调用了 registerBeanDefinitions 方法

5、处理 ImportSelector 接口实现类

image-20230703170552852

同样的,先实例化对象,然后处理 Aware 相关属性注入,然后开始区分了,如果是 DeferredImportSelector 接口的实现类,就封装成 DeferredImportSelectorHolder 对象,添加到 deferredImportSelectors 这个集合里,这个前面有说过,为了延迟调用,如果不是 DeferredImportSelector 接口的实现类,那就是 ImportSelector 接口的实现类,那么就先调用其 selectImports 方法,这个方法会返回 bean 的名字,然后转成 SourceClass 对象,再调用 processImports 方法处理,然后就看 selectImports 方法返回的 bean 是什么类型,再判断处理

总结一下,就是

  • 如果实现了 DeferredImportSelector 接口,那么就存到 deferredImportSelectors 集合里,等待后续处理
  • 如果没有实现 DeferredImportSelector 接口,那么是实现了 ImportSelector 接口,直接调用其 selectImports 方法

所以,实现了 DeferredImportSelector 接口的类的 selectImports 方法,在什么时候调用的呢?

6、处理 DeferredImportSelector 接口实现类

我们回到 ConfigurationClassParser 类的 parse 方法

image-20230703180344801

可以看到最后一步,就是处理所有 DeferredImportSelector 接口的实现类(后面简称deferredImports)的方法,我们跟进去

image-20230703180437893

这个方法里,先把所有的 deferredImports 排序,然后调用 DeferredImportSelectorGroupingHandlerregister 方法,把这些类进行分组,最后再调用 processGroupImports 方法处理,跟进这个方法

image-20230703180836187

可以看到对分组后的map,获取其内容,然后进行循环,获取每个分组中的 deferredImports 循环,调用 processImports 方法,我们看下 grouping.getImports() 返回的是什么

image-20230703181212745

可以看到,正是调用 selectImports 方法的返回,所以处理 DeferredImportSelector 接口的实现类,和处理 ImportSelector 接口的实现类,大致是差不多的,只不过 DeferredImportSelector 要延迟到最后处理,而且会根据不同的顺序和分组,分批处理。
dlerregister方法,把这些类进行分组,最后再调用processGroupImports` 方法处理,跟进这个方法

image-20230703180836187

可以看到对分组后的map,获取其内容,然后进行循环,获取每个分组中的 deferredImports 循环,调用 processImports 方法,我们看下 grouping.getImports() 返回的是什么

image-20230703181212745

可以看到,正是调用 selectImports 方法的返回,所以处理 DeferredImportSelector 接口的实现类,和处理 ImportSelector 接口的实现类,大致是差不多的,只不过 DeferredImportSelector 要延迟到最后处理,而且会根据不同的顺序和分组,分批处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每天进步亿点点的小码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值