1. 前言
@Import 注解 在 Spring 中占据重要地位,是 Spring 的一个重要扩展点。这篇博文我们以案例、源码、应用相结合,来系统的学习一下这个注解
2. 案例演示
2.1 代码准备
2.1.1 创建配置类 AppConfig
@ComponentScan("com.ys")
public class AppConfig {
}
2.1.2 创建启动类 Main
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
}
}
2.2 导入一个普通类
2.2.1 创建实体类 Dog
public class Dog {
}
2.2.2 修改配置类 AppConfig
@ComponentScan("com.ys")
@Import(Dog.class)
public class AppConfig {
}
2.2.3 运行 main 方法,查看运行结果
2.2.4 小结
导入一个普通类,相当于导入一个指定类型的 bean,需要注意的是 @Import 导入的普通类的 beanName 是类的全限定名
2.3 导入一个配置类
2.3.1 创建实体类 User
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private Integer age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.3.2 创建配置类 UserConfig
@Configuration(proxyBeanMethods = false)
public class UserConfig {
@Bean
public User user() {
return new User("anna", 18);
}
}
2.3.3 修改配置类 AppConfig
@ComponentScan("com.ys.entity")
@Import(UserConfig.class)
public class AppConfig {
}
PS:@ComponentScan 注解不扫描 UserConfig 所在包路径
2.3.4 运行 main 方法,查看运行结果
2.3.5 小结
导入一个配置类,它就会以配置类进行解析,会处理 @Bean、@ComponentScan 等注解
2.4 导入一个实现 ImportBeanDefinitionRegistrar 接口的类
2.4.1 创建实体类 Cat
public class Cat {
}
2.4.2 创建实体类 CatRegistrar
public class CatRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName("com.ys.entity.Cat");
registry.registerBeanDefinition("cat", beanDefinition);
}
}
2.4.3 修改配置类 AppConfig
@ComponentScan("com.ys")
@Import(CatRegistrar.class)
public class AppConfig {
}
2.4.4 运行 main 方法,查看运行结果
2.4.5 小结
导入一个实现 ImportBeanDefinitionRegistrar 接口的类,会在解析过程中执行其 registerBeanDefinitions 方法,一般会向 beanFactory 中注册 beanDefinition。beanDefinition是 bean 的建模对象,后期会根据 beanDefinition 的相关属性,按照指定方式实例化 bean
2.5 导入一个实现 ImportSelector 接口的类
2.5.1 创建实体类 RedCar BlackCar
public class RedCar {
}
public class BlackCar {
}
2.5.2 创建实体类 CarImportSelector
public class CarImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.ys.entity.RedCar", "com.ys.entity.BlackCar"};
}
}
2.5.3 修改配置类 AppConfig
@ComponentScan("com.ys")
@Import(CarImportSelector.class)
public class AppConfig {
}
2.5.4 运行 main 方法,查看运行结果
2.5.5 小结
如果导入的类实现 ImportSelector 接口,则会在解析阶段执行 selectImports 方法,返回值是类的全限定名,可能有以下几种情况:
- 类是一个普通类:按照案例 2.2 处理
- 类是一个配置类:按照案例 2.3 处理
- 类实现 ImportBeanDefinitionRegistrar 接口:按照案例 2.4 处理
- 类实现 ImportSelector 接口:循环处理上述三中情况
因为 ImportSelector 的特性,可能会产生一些特殊情况,我们在下文中举例演示
2.6 ImportSelector 接口实现类的 selectImports 方法的返回值也是 ImportSelector 接口实现类
2.6.1 创建实体类 Monkey
public class Monkey {
}
2.6.2 创建实体类 FirstImportSelector SecondImportSelector
public class FirstImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.ys.entity.SecondImportSelector"};
}
}
public class SecondImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.ys.entity.Monkey"};
}
}
2.6.3 修改配置类 AppConfig
@ComponentScan("com.ys")
@Import(FirstImportSelector.class)
public class AppConfig {
}
2.6.4 运行 main 方法,查看运行结果
2.6.5 小结
如果 selectImports 方法的返回值也是 ImportSelector 接口实现类,则会继续处理,所以不存在该类型的 bean(本案例中指 SecondImportSelector)
2.7 @Import 导入的类同时实现 ImportSelector、ImportBeanDefinitionRegistrar 接口
2.7.1 创建实体类 SelectorBean RegistrarBean
public class SelectorBean {
}
public class RegistrarBean {
}
2.7.2 创建实体类 MergeImports
public class MergeImports implements ImportSelector, ImportBeanDefinitionRegistrar {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.ys.entity.SelectorBean"};
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName("com.ys.entity.RegistrarBean");
registry.registerBeanDefinition("registrarBean", beanDefinition);
}
}
2.7.3 修改配置类 AppConfig
@ComponentScan("com.ys")
@Import(MergeImports.class)
public class AppConfig {
}
2.7.4 运行 main 方法,查看运行结果
PS : ImportSelector 方式的 beanName 为类的全限定名, ImportBeanDefinitionRegistrar 方式 的beanName 为手动设置的值
2.7.5 小结
ImportSelector 的优先级高于 ImportBeanDefinitionRegistrar,如果同时实现 ImportSelector、ImportBeanDefinitionRegistrar 接口,只执行 selectImports 方法
3. 源码解析
3.1 ConfigurationClassParser#processImports
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass,
Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// 省略了一大段代码
for (ConfigurationClassParser.SourceClass candidate : importCandidates) {
// 处理ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
// 加载ImportSelector接口实现类
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) {
// 延迟处理,SpringBoot自动配置实现机制(AutoConfigurationImportSelector)
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
} else {
// 执行 ImportSelector#selectImports 方法
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 过滤一些不符合条件的类
Collection<ConfigurationClassParser.SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 循环执行当前方法
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 加载ImportBeanDefinitionRegistrar接口实现类
Class<?> candidateClass = candidate.loadClass();
// 实例化对象
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
// 给ConfigurationClass对象的相关属性赋值,后期再处理
// 相关源码ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars
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), exclusionFilter);
}
}
}
流程图展示如下:
3.2 通过源码分析案例 2.2、2.3、2.6、2.7
3.2.1 案例 2.2、2.3
案例 2.2、2.3 可以归位一类,不管类上是否存在 @Component 相关注解,都会被当成配置类解析。主要区别在于如果存在 @Component 相关注解,既可以被 @ComponentScan 注解处理,也可以被 @Import 注解处理,主要看解析顺序了。有兴趣的读者测试一下,将案例 2.3 UserConfig 类上的 @Configuration 注解注释掉,同样可以获取类型为 User 的 bean
3.2.2 案例 2.6
案例2.6 会循环执行 processImports 方法,直到 selectImports 方法返回的类是配置类(普通类)或者 ImportBeanDefinitionRegistrar 接口的实现类
3.2.3 案例 2.7
processImports 方法分为三个分支,实现 ImportSelector 接口优先级最高
4. @Import 注解的应用
4.1 SpringBoot 自动配置 (相关源码按照以下步骤切入)
- @SpringBootApplication
- @EnableAutoConfiguration
- @Import(AutoConfigurationImportSelector.class)
- AutoConfigurationImportSelector#selectImports
相关博文:SpringBoot 自动配置原理
4.2 @EnableAspectJAutoProxy(相关源码按照以下步骤切入)
- @EnableAspectJAutoProxy
- @Import(AspectJAutoProxyRegistrar.class)
- AspectJAutoProxyRegistrar#registerBeanDefinitions
4.3 小结
很多 @EnableXxx 这样的注解,都是依靠 @Import 来扩展了,流程基本都一样。还有一个套路就是最后注入的 bean 有一个类型是 BeanPostProcessor,该 BeanPostProcessor 可以对符合特征的 bean 生效,比如说 @EnableTransactionManagement、@EnableConfigurationProperties 注解。
BeanPostProcessor 相关博文:Spring之BeanPostProcessor