结论
@Import
注解可以导入三种类型的类
- 导入普通类,此时直接将普通类交给
Spring
管理 - 导入
ImportSelector
实现类中String[] selectImports(AnnotationMetadata importingClassMetadata);
方法返回的类 - 导入
ImportBeanDefinitionRegistrar
实现类中registerBeanDefinitions
方法注入的类
代码示例
以下代码每种方式使用的类都在同一个包下( @Configuration
默认会扫描当前包和子包 )。
Import的三种使用方式
引入普通类
java
// 普通类
public class Student {
public void sayHello() {
System.out.println("test hello");
}
}
// 配置类
@Configuration
@Import(Student.class)
public class ImportConfig {
}
// 主类
public class ImportMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ImportConfig.class);
Student student = context.getBean(Student.class);
student.sayHello(); // 会打印 "test hello"
}
}
结合ImportSelector
java
// 普通类
public class Student {
public void sayHello() {
System.out.println("test hello");
}
}
// 配置类
@Configuration
@Import(CusImportSelector.class) // 这里导入的是实现了 ImportSelector 接口的类
public class ImportConfig {
}
// 自定义实现 ImportSelector 接口的类
public class CusImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 返回的数组就是需要导入的类全限定名
return new String[]{"org.example.imports.Student"};
}
}
// 主类
public class ImportMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ImportConfig.class);
Student student = context.getBean(Student.class);
student.sayHello(); // 会打印 "test hello"
}
}
结合 ImportBeanDefinitionRegistrar
java
// 普通类
public class Student {
public void sayHello() {
System.out.println("test hello");
}
}
// 配置类
@Configuration
@Import(CusImportBeanDefinitionRegistrar.class) // 这里导入的是实现了 ImportSelector 接口的类
public class ImportConfig {
}
// 自定义实现 ImportBeanDefinitionRegistrar 接口的类
public class CusImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Student.class);
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
String beanName = importBeanNameGenerator.generateBeanName(beanDefinition, registry);
registry.registerBeanDefinition(beanName, beanDefinition);
}
}
// 主类
public class ImportMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ImportConfig.class);
Student student = context.getBean(Student.class);
student.sayHello(); // 会打印 "test hello"
}
}
源码流程解析
@Import
的解析实际是通过 BeanFactoryPostProcessor
实现的, 在 [[BeanFacotry的后置处理器详解]] 提到 @ComponentScan
是通过 ConfigurationClassPostProcessor
处理的,处理流程如下:
bash
AbstractApplicationContext#refresh
AbstractApplicationContext#invokeBeanFactoryPostProcessors
PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
ConfigurationClassParser#parse(Set<BeanDefinitionHolder>)
ConfigurationClassParser#doProcessConfigurationClass
最终是会调用到 ConfigurationClassParser#doProcessConfigurationClass
方法,接下来就看看这个方法
java
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 省略
// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.propertySourceRegistry != null) {
this.propertySourceRegistry.processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 这里是处理 @ComponentScan 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)) {
// 省略
}
// 处理 @Import Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 省略 处理 @ImportResource 等 Process any @ImportResource annotations
return null;
}
processImports
方法
java
// ConfigurationClassParser 类, 该方法只列出了重要方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
for (SourceClass candidate : importCandidates) {
// 导入的是 ImportSelector 类型的处理流程
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector deferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
// 导入的是 ImportBeanDefinitionRegistrar 类型的处理流程
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 =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// 既不是 ImportSelector 也不是 ImportBeanDefinitionRegistrar 类型的处理流程,当作 普通的 @Configuration 类处理
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
导入的是普通类是的处理流程
从上面源码可以看到,如果导入的是普通的类(不是 ImportSelector
也不是 ImportBeanDefinitionRegistrar
类型)则将这个普通类按照含有 @Configuration
注解的类一样进行处理, 如果是按照 @Configuration
注解的类进行处理,会将普通类变成 ConfigurationClass
类型
导入的是 ImportSelector
类型
对于导入的是 ImportSelector
类型处理代码如下
java
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector deferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
- 先调用
selectImports
方法获取导入的类名数组 - 调用
asSourceClasses
方法将类名数组转换为SourceClass
集合 - 调用
processImports
方法处理SourceClass
数组, 这里就是递归调用了,递归的第一层是ImportSelector
类型的类,递归的第二层就是selectImports
方法中返回的普通的类了,这个就和Import
导入的是普通类一样的处理流程,也就是最终是按照@Configuration
注解的类处理
导入的是 ImportBeanDefinitionRegistrar
类型
对于导入的是 ImportBeanDefinitionRegistrar
类型处理代码如下
java
if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// configClass 就是 ConfigurationClass
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
java
/**
* ConfigurationClass 类
*
* private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars = new LinkedHashMap<>();
* /
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}
最终是将 ImportBeanDefinitionRegistrar
实现类放入了 ConfigurationClass
类中的 importBeanDefinitionRegistrars
属性中,那么最终是什么时候注册成 BeanDefinition
的呢?
这个就得回到 org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
方法中了, 这里通过流程图梳理一下解析和注册两个步骤的流程
- 整体逻辑是在
ConfigurationClassPostProcessor
的processConfigBeanDefinitions()
方法中执行的 - 对于
@ComponentScan
,@Import
等解析是在ConfiguraionClassParser
的doProcessConfigurationClass()
方法中解析的 - 对于
ImportBeanDefinitionRegistrar
类型的类在第二步解析完成之后放入了ConfigurationClass
类型(就是将ImportBeanDefinitionRegistrar
类型的实现类转换成ConfigurationClass
类型而已)中的importBeanDefinitionRegistrars
属性中 - 最后在
ConfigurationClassBeanDefinitionReader
类的loadBeanDefinitionsFromRegistrars()
方法中会进行BeanDefinition
的注册
至此,解析和注册两个步骤的流程就解析完了