当前位置: 首页 > news >正文

@Import原理与实战

文章目录

  • 前言
  • 一、导入普通类
  • 二、导入ImportSelector实现类
  • 三、导入ImportBeanDefinitionRegistrar实现类
  • 四、@Import注解的解析
    • 4.1、解析实现ImportSelector的候选bean
    • 4.2、解析实现ImportBeanDefinitionRegistrar的候选bean
    • 4.3、DeferredImportSelector的特殊处理
  • 总结


前言

  @Import是Spring框架提供的一个核心注解,主要用于在配置类中引入其他配置类或组件。通过在类上标注@Import注解,可以将其value属性中指定的类注册到Spring容器中,从而实现配置的模块化和灵活组合。

一、导入普通类

  @Import注解会将其中导入的普通类,注册成bean放入到Spring容器中。

public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);System.out.println(context.getBean(MyService.class));}
}class MyService {
}@Configuration
@Import(MyService.class) 
class Config {}

  运行结果:

com.itbaima.importdemo.demo1.MyService@55183b20

二、导入ImportSelector实现类

  解析@Import注解时会执行实现了ImportSelector的类的selectImports方法,将返回的数组中的类注册成bean。

public class Demo2 {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config1.class);System.out.println(context.getBean(MyService.class));}
}@Configuration
@Import(MyImport.class)
class Config1{}class MyImport implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.itbaima.importdemo.demo1.MyService"};}
}

  运行结果

com.itbaima.importdemo.demo1.MyService@33d512c1

三、导入ImportBeanDefinitionRegistrar实现类

  实现了ImportBeanDefinitionRegistrar的类,可以自己注册bean定义,以及从已有的bean定义中获取指定的bean,进行修改,自由度是最高的。

public class Demo3 {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);System.out.println(context.getBean("orderService"));}
}@Configuration
@Import({MyBeanDefinitionRegistrar.class})
class Config {
}class MyBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {GenericBeanDefinition bd = new GenericBeanDefinition();bd.setBeanClass(MyService.class);registry.registerBeanDefinition("orderService", bd);}
}class MyService {
}

  运行结果:

com.itbaima.importdemo.demo2.MyService@2f9f7dcf

四、@Import注解的解析

  @Import注解的解析,体现在refresh方法中的invokeBeanFactoryPostProcessors(调用所有bean工厂后置处理器)这一步:
在这里插入图片描述
  ConfigurationClassPostProcessor,用于配置类的解析:
在这里插入图片描述
  在org.springframework.context.annotation.ConfigurationClassParserprocessImports中完成对于@Import注解的解析:
在这里插入图片描述

4.1、解析实现ImportSelector的候选bean

  对于候选bean实现了ImportSelector接口的处理:

  1. 利用JVM的类加载机制,对于候选bean进行加载。
  2. 使用Spring提供的工具方法instantiateClass反射创建这个ImportSelector实例,并注入必要的环境参数。
  3. 如果候选bean实现了DeferredImportSelector,则将其加入deferredImportSelectors的集合中,延迟处理
  4. 候选bean实现的是ImportSelector接口,就调用目标类重写的selectImports方法,决定要导入哪些类名。然后把类名数组转换成 SourceClass 的集合,并应用排除过滤器。

在这里插入图片描述
  调用目标类重写的selectImports方法,传入的参数就是加入了@Configuration注解的配置类的元信息。最后一步递归调用processImports方法很关键:假设当前第一次调用processImports方法,解析出的类路径是com.itbaima.importdemo.demo1.MyService,那么在递归调用processImports方法时,再次进入
在这里插入图片描述
  循环中进行解析时,由于MyService没有实现ImportSelectorImportBeanDefinitionRegistrar注解,就会进入最后的else分支:(@Import导入的普通bean,进入的也是该分支)
在这里插入图片描述
  在最后的else分支中,还会去递归调用processConfigurationClass,将MyService当做配置类去解析,最终将解析完成的放入到ConfigurationClass的集合中。(注意,通过Import解析出的类路径下的bean,bean名称为空,后续需要再次进行处理。)
在这里插入图片描述
  this.reader.loadBeanDefinitions(configClasses);这一步,会对于@Import导入的普通bean进行处理:
在这里插入图片描述
  生成一个beanName,并且将bean定义注册到bean工厂中:
在这里插入图片描述

4.2、解析实现ImportBeanDefinitionRegistrar的候选bean

  @Import导入实现ImportBeanDefinitionRegistrar的候选bean的解析逻辑,在else…if的分支中,同样是通过JVM的类加载机制,加载候选类的class类,然后使用Spring提供的工具方法instantiateClass反射创建这个ImportBeanDefinitionRegistrar实例,并注入必要的环境参数。
在这里插入图片描述
  最后将其加入到ConfigurationClassimportBeanDefinitionRegistrars属性中:
在这里插入图片描述
  它的解析同样是在processConfigBeanDefinitionsthis.reader.loadBeanDefinitions(configClasses);中:
在这里插入图片描述
  回调用户重写的registerBeanDefinitions的逻辑。
在这里插入图片描述

4.3、DeferredImportSelector的特殊处理

  用户实现ImportSelector时,有一种特殊的情况,即用户实现了DeferredImportSelectorDeferredImportSelectorImportSelector的子类。在该分支中,仅仅是先将其加入DeferredImportSelectorHandlerdeferredImportSelectors属性中:
在这里插入图片描述
  DeferredImportSelectorHandlerdeferredImportSelectors属性是一个集合:
在这里插入图片描述
  最终会在所有bean解析完成后再去进行解析:
在这里插入图片描述
  该机制主要应用于Spring Boot的自动配置场景,其核心作用是确保用户自定义的Bean能够优先于自动配置的Bean执行。由于spring.factories中定义的自动配置Bean通常采用条件装配机制,当容器中已存在用户自定义的同类型Bean时,系统将不再重复装配。这正是通过@Bean注解添加的Bean能够覆盖默认Bean的原因
在这里插入图片描述
在这里插入图片描述
  在doProcessConfigurationClass中,默认的@Bean的注解,是后于@Import注解解析的。如果你不显式允许覆盖,Spring 在注册 BeanDefinition 时会抛出异常或者忽略重复的注册。
在这里插入图片描述
  但是对于DeferredImportSelector的解析,是在doProcessConfigurationClass的外层parse方法中执行的,后于@Bean注解的解析。

总结

  @Import注解是为了替换掉配置文件中的import标签,主要是为了导入第三方的配置类,除此之外:

  1. 可以在配置类中指定@Import注解,将其中的类注入到容器中。
  2. 可以在配置类中指定@Import注解,同时其中的类实现了Import Selector接口,会执行重写的selectImports方法,并且将其中指定的路径的对象注入到容器中。
  3. 如果导入的类型实现了Import BeanDefinitionRegistrar,则可以自己注册 BeanDefinition,自由度更高。

相关文章:

  • 自定义protoc-gen-go生成Go结构体,统一字段命名与JSON标签风格
  • Go语言系统监控实战:gopsutil库全面解析与应用
  • 75Qt窗口_Qt窗口概览
  • Redis集群模式之主从复制模式(2)
  • 轻量级的Windows系统优化与个性化解决方案
  • 汽车车载软件平台化项目规模颗粒度选择的一些探讨
  • JS Day05
  • 幂等性的七大解决方案
  • 如何利用 audit2allow 工具调试SELinux日志
  • Spring类型转换器相关接口和实现原理
  • LLMs 系列科普文(8)
  • 免费批量PDF转Word工具
  • MyBatis原理剖析(一)
  • Vue ⑤-自定义指令 || 插槽
  • SCADA|RESTful学习,Apipost通过GET获取KingSCADA实时数据
  • LeetCode - 53. 最大子数组和
  • 哈佛总线架构是什么?
  • Linux(Centos 7.6)命令详解:which
  • Web前端开发:JavaScript中的eval()函数
  • C语言基础面试问答
  • 介绍个人网站的ppt怎么做/自媒体十大平台
  • 惠州市人民政府门户网站/网络广告推广方法
  • 开宾馆做独家网站好吗/推广渠道平台
  • 雄安免费网站建设方案/北京网站推广公司
  • 湖州做网站公司哪家好/网站域名备案查询
  • 哈尔滨网页设计公司/西安百度网站排名优化