@Import注解 -【Spring底层原理】

@Import注解也是用来给容器注册组件的,使用@Import注解快速给容器中导入一个组件有三种方法

  1. 导入@Configuration注解的配置类使用@Import(要导入到容器中的组件):容器中就会自动注册这个组件,ID默认为全类名
  1. 导入ImportSelector的实现类:通过实现ImportSelector类,实现selectImports方法,返回需要导入组件的全类名数组
  1. 导入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

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

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

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 里面的类,流程如下:

  1. 定义一个 visited 的集合,用作 是否已经 判断过的标志

  2. 这里就是获取sourceClass 上面的 所有的 annotation,并挨个判断, 如果不是 @import ,那就 进一步递归 调用 对应的 annotation,直到全部结束

  3. 加载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开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
1242)]

[外链图片转存中…(img-5KjEZaLS-1715664951243)]

[外链图片转存中…(img-DFEQJg8H-1715664951243)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 30
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值