@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();
}
}
}
从上面的源码可以看出:
- ImportSelector会被直接反射实例化,然后调用selectImports()方法,将返回值放入到importStack中,注意ImportSelector本身没有被Spring容器所管理。然后又调用递归processImports方法,这样会进入本方法的else,当selectImports()导入的类当成一个配置类来处理。
- ImportBeanDefinitionRegistrar也会被直接反射实例化,然后加入到ConfigClass.importBeanDefinitionRegistrars属性中,ImportBeanDefinitionRegistrar.registerBeanDefinitions()此时并没有被调用。
- 通过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));
}