@Import注解也是用来给容器注册组件的,使用@Import注解快速给容器中导入一个组件有三种方法
- 导入
@Configuration
注解的配置类使用@Import
(要导入到容器中的组件):容器中就会自动注册这个组件,ID默认为全类名
- 导入
ImportSelector
的实现类:通过实现ImportSelector
类,实现selectImports
方法,返回需要导入组件的全类名数组
- 导入
ImportBeanDefinitionRegistrar
的实现类:通过实现ImportBeanDefinitionRegistrar
类,实现registerBeanDefinitions
方法手动注册Bean到容器中
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
通过注解源码可以看到,@Import注解作用在类上,并且参数可以是class类型的数组,从这里可以看出可以使用@Import注解一次导入多个组件到容器中
二、实例分析
从上面的注解用法来看,使用@Import
注解给容器导入组件有三种方法,并且该注解作用在方法上,一次可以导入多个组件,因此,这里我们直接将三种方法都放在一个@Import
注解来进行导入。如下案例需求:使用方法一注入User
类、使用方法二注入Person
类、使用方法三注入Animal
类。
【1】导入@Configuration
注解的配置类使用@Import
// 启动类,通过打印容器中的Bean来判断是否注入
@Test
public void TestMain(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
String[] beanNames = applicationContext.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
// 待注入的User
public class User {
}
// 配置类
@Configuration
@Import(User.class) //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}
通过在配置类上使用@Import
注解,将User
给注入进容器中,运行启动类,可以看到容器中有User
对象:
image-20210226164625069
【2】导入ImportSelector
的实现类
导入ImportSelector
的实现类需要实现ImportSelector
类,自定义逻辑返回需要导入的组件,返回的字符串数组即是要注入的组件,添加修改如下代码:
// ImportSelector实现类
public class MyImportSelector implements ImportSelector {
/**
* @description 获取要导入到容器的组件全类名
* @author ONESTAR
* @date 2021/2/25 15:49
* @param annotationMetadata:当前标注@Import注解类的所有注解信息
* @throws
* @return java.lang.String[]
*/
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{“bean.Person”};
}
}
// 待注入的Person
public class Person {
}
// 配置类
@Configuration
@Import({User.class, MyImportSelector.class}) //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}
在ImportSelector
实现类中获取要导入到容器的组件全类名,这里将ImportSelector
实现类在配置类中使用@Import
注解进行配置,运行启动类,可以看到容器中有Person
对象:
image-20210227151242060
【3】导入ImportBeanDefinitionRegistrar
的实现类
导入ImportBeanDefinitionRegistrar
的实现类需要实现ImportBeanDefinitionRegistrar
类,通过实现registerBeanDefinitions
方法手动注册Bean到容器中,添加修改如下代码:
// ImportBeanDefinitionRegistrar实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
// 指定Bean的名称
RootBeanDefinition beanDefinition = new RootBeanDefinition(Animal.class);
beanDefinitionRegistry.registerBeanDefinition(“Animal”, beanDefinition);
}
}
// 待注入的Animal
public class Animal {
}
// 配置类
@Configuration
@Import({User.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) //使用@Import导入组件,ID默认是组件的全类名
public class AppConfig {
}
通过ImportBeanDefinitionRegistrar
的实现类进行手动注册添加Bean,并在配置类中使用@Import
注解进行配置,运行启动类,可以看到容器中有Animal
对象:
image-20210227153057676
三、源码追踪
参考:https://blog.csdn.net/mamamalululu00000000/article/details/86711079
通过@Configuration
注解,会进入到doProcessConfigurationClass
方法,此时解析的是appConfigure
,在doProcessConfigurationClass
方法里面,有个执行@Import
注解的方法,即processImports
this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);
@Import
注解执行的时机,解析配置类的时候,由ConfigurationClassParser
当中的processImports
来处理,在分析processImports
方法之前,咱们先来看看参数getImports
方法:
【1】getImports方法
进入源码查看方法,这个方法就是获取所有的@import
里面的类,流程如下:
-
定义一个 visited 的集合,用作 是否已经 判断过的标志
-
这里就是获取sourceClass 上面的 所有的 annotation,并挨个判断, 如果不是 @import ,那就 进一步递归 调用 对应的 annotation,直到全部结束
-
加载sourceClass 里面 的@Import annotation 里面对应的类名 ,最后返回
// 获取所有的@import
里面的类
private Set<ConfigurationClassParser.SourceClass> getImports(ConfigurationClassParser.SourceClass sourceClass) throws IOException {
Set<ConfigurationClassParser.SourceClass> imports = new LinkedHashSet();
Set<ConfigurationClassParser.SourceClass> visited = new LinkedHashSet();
this.collectImports(sourceClass, imports, visited);
return imports;
}
// 这里就是获取sourceClass 上面的 所有的 annotation, 如果不是 @import ,那就 进一步递归 调用 对应的 annotation,直到全部结束
private void collectImports(ConfigurationClassParser.SourceClass sourceClass, Set<ConfigurationClassParser.SourceClass> imports, Set<ConfigurationClassParser.SourceClass> visited) throws IOException {
if (visited.add(sourceClass)) {
Iterator var4 = sourceClass.getAnnotations().iterator();
while(var4.hasNext()) {
ConfigurationClassParser.SourceClass annotation = (ConfigurationClassParser.SourceClass)var4.next();
String annName = annotation.getMetadata().getClassName();
if (!annName.equals(Import.class.getName())) {
this.collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), “value”));
}
}
【2】processImports 方法
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
写在最后
还有一份JAVA核心知识点整理(PDF):JVM,JAVA集合,JAVA多线程并发,JAVA基础,Spring原理,微服务,Netty与RPC,网络,日志,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存,Hadoop,Spark,Storm,YARN,机器学习,云计算…
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
还有一份JAVA核心知识点整理(PDF):JVM,JAVA集合,JAVA多线程并发,JAVA基础,Spring原理,微服务,Netty与RPC,网络,日志,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存,Hadoop,Spark,Storm,YARN,机器学习,云计算…
[外链图片转存中…(img-4TATHuNO-1713304741324)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!