@Import是一个注解,专门用来导入实现了ImportSelector和ImportBeanDefinitionRegistrar接口的类,当然也可以用来导入普通的Bean。

@Import的使用

@Import+普通Bean

package com.morris.spring.demo.annotation;

import com.morris.spring.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

/**
 * @Import + 普通的Bean
 */
@Import(User.class)
public class ImportSimpleBeanDemo {

	@Test
	public void testImportSingleBean() {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportSimpleBeanDemo.class);
		User user = applicationContext.getBean(User.class);
		System.out.println(user);
	}

}

运行结果如下:

User(username=null, age=0)

可见User类上未加@Component注解,可以通过@Import注解将User对象注入到Spring容器中。

@Import+ImportSelector

ImportSelectorBean实现了ImportSelector接口的selectImports()方法,该方法的返回值就是返回需要实例化的Bean的全限定名。

package com.morris.spring.entity.imports;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class ImportSelectorBean implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.morris.spring.entity.User"};
	}
}

ImportSelectorBeanDemo中使用了@Import注解注入ImportSelector。

package com.morris.spring.entity.imports;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

public class ImportSelectorBean implements ImportSelector {
	/**
	 * importingClassMetadata为导入ImportSelectorBean的类上面的注解信息,这里是ImportSelectorBeanDemo
	 * @param importingClassMetadata
	 * @return
	 */
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		return new String[]{"com.morris.spring.entity.User"};
	}
}

运行结果如下:

User(username=null, age=0)
false

可见User类上未加@Component注解,可以通过@Import+ImportSelector将User对象注入到Spring容器中,但是ImportSelector对象本身并未被Spring管理。

@Import+ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrarBean实现了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions()方法,该方法可以通过BeanDefinitionRegistry参数向容器中注入BeanDefinition,从而实现将Bean加入到Spring容器中。

package com.morris.spring.entity.imports;

import com.morris.spring.entity.User;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class ImportBeanDefinitionRegistrarBean implements ImportBeanDefinitionRegistrar {


	/**
	 * @param importingClassMetadata  为导入本类上面的注解信息,这里是ImportBeanDefinitionRegistrarBeanDemo
	 * @param registry                可以向容器中注入BeanDefinition
	 * @param importBeanNameGenerator 可以为导入的Bean生成beanName
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {

		System.out.println(importingClassMetadata.getAnnotationTypes());

		GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
		genericBeanDefinition.setBeanClass(User.class);
		registry.registerBeanDefinition("user", genericBeanDefinition);
	}

}

ImportBeanDefinitionRegistrarBeanDemo中使用了@Import注解注入ImportBeanDefinitionRegistrarBean。

package com.morris.spring.demo.annotation;

import com.morris.spring.entity.User;
import com.morris.spring.entity.imports.ImportBeanDefinitionRegistrarBean;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Import;

/**
 * @Import + ImportBeanDefinitionRegistrar
 */
@Import(ImportBeanDefinitionRegistrarBean.class)
public class ImportBeanDefinitionRegistrarBeanDemo {

	@Test
	public void testImportSingleBean() {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ImportBeanDefinitionRegistrarBeanDemo.class);
		User user = applicationContext.getBean(User.class);
		System.out.println(user);
		System.out.println(applicationContext.containsBean("importBeanDefinitionRegistrarBean"));
	}

}

运行结果如下:

User(username=null, age=0)
false

可见User类上未加@Component注解,可以通过@Import+ImportBeanDefinitionRegistrar将User对象注入到Spring容器中,但是ImportBeanDefinitionRegistrar对象本身并未被Spring管理。

源码分析

带着下面的问题进行分析:

  • ImportSelector和ImportBeanDefinitionRegistrar都是通过@Import注解导入,那么@Import注解所在的类是何时被扫描的?
  • ImportSelector和ImportBeanDefinitionRegistrar接口的方法是何时被调用的?
  • ImportSelector和ImportBeanDefinitionRegistrar对象为什么没有被加入到Spring容器中?

@Import的扫描

首先会从容器中取出所有的配置类(@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean),因为这些类上面可能会有@Import注解。

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	String[] candidateNames = registry.getBeanDefinitionNames();

	// 先收集有@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean的BD
	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
			}
		}
		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
			// 进来需要@Component、@ComponentScan、@Import、@ImportResource、@Bean
			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}
... ...

获取类上面的@Import注解:
org.springframework.context.annotation.ConfigurationClassParser#getImports

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
	Set<SourceClass> imports = new LinkedHashSet<>();
	Set<SourceClass> visited = new LinkedHashSet<>();
	collectImports(sourceClass, imports, visited);
	return imports;
}

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
		throws IOException {
	if (visited.add(sourceClass)) {
		for (SourceClass annotation : sourceClass.getAnnotations()) {
			String annName = annotation.getMetadata().getClassName();
			if (!annName.equals(Import.class.getName())) {
				collectImports(annotation, imports, visited);
			}
		}
		imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
	}
}

然后类上面有@Import注解的才会调用processImports()方法。
org.springframework.context.annotation.ConfigurationClassParser#processImports

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
		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)) {
					// 处理ImportSelector
					// Candidate class is an ImportSelector -> delegate to it to determine imports
					Class<?> candidateClass = candidate.loadClass(); // 加载ImportSelector
					ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
							this.environment, this.resourceLoader, this.registry); // 反射实例化ImportSelector
					Predicate<String> selectorFilter = selector.getExclusionFilter();
					if (selectorFilter != null) {
						exclusionFilter = exclusionFilter.or(selectorFilter);
					}
					if (selector instanceof DeferredImportSelector) {
						// 特殊处理DeferredImportSelector
						this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
					}
					else {
						// 调用selectImports方法
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
						// 又递归,实际上会进入下面的else
						processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
					}
				}
				else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
					// Candidate class is an ImportBeanDefinitionRegistrar ->
					// delegate to it to register additional bean definitions
					// 处理ImportBeanDefinitionRegistrar
					Class<?> candidateClass = candidate.loadClass();
					// 实例化ImportBeanDefinitionRegistrar
					ImportBeanDefinitionRegistrar registrar =
							ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
									this.environment, this.resourceLoader, this.registry);
					// 这里只加入到configClass的importBeanDefinitionRegistrars容器中,并没有调用registerBeanDefinitions方法
					// registerBeanDefinitions方法是在哪里调用的呢?
					configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
				}
				else {
					// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
					// process it as an @Configuration class
					// 通过ImportSelector导入的类会进入这里,通过@Import注入的普通类会进入这里
					// 通过ImportSelector导入的类会从这里加入到importStack
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					// 当成一个配置类来循环处理
					processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
				}
			}
		}
		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();
		}
	}
}

从上面的源码可以看出:

  1. ImportSelector会被直接反射实例化,然后调用selectImports()方法,将返回值放入到importStack中,注意ImportSelector本身没有被Spring容器所管理。然后又调用递归processImports方法,这样会进入本方法的else,当selectImports()导入的类当成一个配置类来处理。
  2. ImportBeanDefinitionRegistrar也会被直接反射实例化,然后加入到ConfigClass.importBeanDefinitionRegistrars属性中,ImportBeanDefinitionRegistrar.registerBeanDefinitions()此时并没有被调用。
  3. 通过ImportSelector导入的类和通过@Import注入的普通类会进入else代码块,当成一个配置类来循环处理,processConfigurationClass方法的最后一行会将所有的配置类加入到容器configurationClasses中。

那么configurationClasses中的配置类何时加入到Spring容器中呢?

ImportBeanDefinitionRegistrar.registerBeanDefinitions方法是在哪里调用的呢?

loadBeanDefinitions

org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
		// 从配置类中加载BeanDefinition
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}

private void loadBeanDefinitionsForConfigurationClass(
		ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}
	if (configClass.isImported()) {
		// 通过ImportSelector导入的类和通过@Import注入的普通类在这里注入BeanDefinition
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		// 注入@Bean注解的BeanDefinition
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	// ImportBeanDefinitionRegistrar.registerBeanDefinitions的调用
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

通过ImportSelector导入的类和通过@Import注入的普通类在这里注入BeanDefinition

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
	AnnotationMetadata metadata = configClass.getMetadata();
	AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
	ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
	configBeanDef.setScope(scopeMetadata.getScopeName());
	String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
	AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
	configClass.setBeanName(configBeanName);
	if (logger.isTraceEnabled()) {
		logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
	}
}

ImportBeanDefinitionRegistrar.registerBeanDefinitions()方法的调用:

private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
	// 调用ImportBeanDefinitionRegistrar.registerBeanDefinitions()
	registrars.forEach((registrar, metadata) ->
			registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}