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

Spring Boot自动装配原理

Spring Boot自动装配原理

一、什么是自动装配?

自动装配通过分析类路径下的依赖、Bean的定义以及其他配置信息,自动配置Spring应用程序的配置类和Bean,以减少开发人员的配置工作,提高开发效率。

    Spring Boot的自动装配实际上是从 META-INF/spring.factories 文件中获取到对应的需要进行自动装配的类,并生成相应的Bean对象,然后将它们交给Spring容器进行管理。
    没有 Spring Boot 的情况下,如果我们需要引入第三方依赖,需要手动配置。

二、Spring Boot如何实现自动装配?

    通过SpringBoot项目启动类上的@SpringBootApplication注解来实现。
@SpringBootApplication注解核心源码:

//自定义注解相关注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//自动装配相关注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}
), @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}

@SpringBootApplication主要由 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解组成。

  • @SpringBootApplication:标注在某个类上,表示这是一个Spring Boot的配置类;
  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制
  • @ComponentScan:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

@EnableAutoConfiguration 是实现自动装配的主要注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}
  • @AutoConfigurationPackage:将主程序类所在包及所有子包下的组件扫描到Spring容器中。
  • @Import:
        ① 导入Bean
        ② 导入配置类
        ③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类
        ④ 导入 ImportBeanDefinitionRegistrar 实现类。

由源码可见自动装配的核心功能与引入的 AutoConfigurationImportSelector 类有关。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}

    在AutoConfigurationImportSelector 类中发现它实现了ImportSelector接口,也就实现了这个接口中的 selectImports 方法,该方法主要用于获取所有符合条件的类的全限定类名,并以字符串数组返回,这些类需要被加载到 IoC 容器中。

    该方法主要通过调用**getAutoConfigurationEntry()**方法获取AutoConfigurationEntry对象,这个方法主要负责加载自动配置类的。

    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {AnnotationAttributes attributes = this.getAttributes(annotationMetadata);List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);configurations = this.removeDuplicates(configurations);Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}}

该方法通过调用 List configurations = getCandidateConfigurations(annotationMetadata, attributes) 获取到所有需要导入到容器中的配置类。
在这里插入图片描述
    具体是在该类的 getCandidateConfigurations 方法中调用了 SpringFactoriesLoader类的 loadFactoryNames 方法获取所有自动转配类名,loadSpringFactories() 方法从META-INF/spring.factories加载自动装配类。
    最后按照条件装配(@Conditional)最终会按需配置。

    加载 spring.factories 中的配置,但不是每次启动都会加载其中的所有配置,会有一个筛选的过程,去掉重复的配置。

总结

  1. Spring Boot项目中@SpringBootApplication注解实现自动装配,这个注解是对三个注解进行了封装:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan,
    其中@EnableAutoConfiguration是实现自动化配置的核心注解。
  2. 该注解通过@Import注解导入AutoConfigurationImportSelector,这个类实现了一个导入器接口ImportSelector。在该接口中重写了一个方法selectImports。
  3. selectImports方法的返回值是一个数组,数组中存储的就是要被导入到spring容器中的类的全限定名。在AutoConfigurationImportSelector类中重写了这个方法。
  4. 该方法内部就是读取了项目的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名。
  5. 在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。

三、Condition

Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。
    @Conditional 有一个属性 value,其类型是 Condition 数组。组件必须匹配数组中所有的 Condition,才可以被注册。
    Condition 是一个函数式接口,只有一个 matches 方法,返回 true 则表示条件匹配。matches 方法的两个参数分别是上下文信息和注解的元信息,从这两个参数中可以获取到 IOC 容器和当前组件的信息,从而判断条件是否匹配。

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency>
  1. 没有添加坐标前,发现为空,报错
  2. 有添加坐标前,发现有对象
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(UserConfig.class)
public @interface EnableUser {
}@Configuration
public class UserConfig {@Beanpublic User user(){return new User();}
}
@SpringBootApplication
@EnableScheduling
@Import(UserConfig.class)
@EnableAsync
public class Springbootcondition03Application {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(Springbootcondition03Application.class, args);User user = context.getBean(User.class);System.out.println(user);}}
@Configuration
public class UserConfig {@Bean
//    @Conditional(value = ClassCondition.class)
//    public User user(){
//        return new User();
//    }@ConditionOnClass(value = {"com.alibaba.fastjson.JSON","redis.clients.jedis.Jedis"})public User user(){return new User();}@Bean@ConditionalOnProperty(name = "k1",havingValue = "v2")public User user2(){return new User();}
}public class ClassCondition implements Condition {//Params://context – 上下文对象。用于获取环境,IOC容器,ClassLoader对象 metadata – 注解元对象。 可以用于获取注解定义的属性值//Returns:@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//        boolean flag = true;
//        try {
//            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
//        } catch (ClassNotFoundException e) {
//            flag = false;
//        }
//        return flag;Map<String,Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());System.out.println(map);String[] value = (String[]) map.get("value");boolean flag = true;try {for(String className : value){Class<?> cls = Class.forName(className);}} catch (ClassNotFoundException e) {flag = false;}return flag;}
}@Target({ElementType.TYPE,ElementType.METHOD})//可以修饰在类与方法上
@Retention(RetentionPolicy.RUNTIME)//注解生效节点runtime
@Documented//生成文档
@Conditional(value = ClassCondition.class)
public @interface ConditionOnClass {String[] value();//设置此注解的属性redis.clients.jedis.Jedis
}@SpringBootApplication
public class SpringbootCondition01Application {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootCondition01Application.class, args);Object user = context.getBean("user");System.out.println(user);}}

Condition – 小结
自定义条件:

  1. 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回 boolean值 。
    matches 方法两个参数:

    • context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
    • metadata:元数据对象,用于获取注解属性。
  2. 判断条件: 在初始化Bean时,使用 @Conditional(条件类.class)注解
    SpringBoot 提供的常用条件注解:
    以下注解在SpringBoot-Autoconfigure的condition包下

    • ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
    • ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
    • ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
    • ConditionalOnBean:判断环境中有对应Bean才初始化Bean

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • MongoDB - 聚合阶段 $group 的使用
  • 网络战时代的国家安全:策略、技术和国际合作
  • 数据库开发:MySQL基础(二)
  • 7-25学习笔记
  • 数据结构:(1)线性表
  • easyExcel 3.x以上版本导入数据后,再把错误信息导出,外加自定义RGB背景色、行高、宽度等
  • PSINS工具箱函数介绍——insplot
  • .NET Framework 3.5安装教程
  • js抓取短信验证码发送
  • 通知:PostgreSQL证书领取(PCA114)# PG 证书
  • Golang 知识结构图
  • c生万物系列(封装)
  • Android SurfaceFlinger——GraphicBuffer获取内存信息(三十一)
  • 【H.264】H.264详解(二)—— H264视频码流解析示例源码
  • 使用协程实现调用接口 验证抽奖概率
  • CSS 三角实现
  • es6要点
  • Java深入 - 深入理解Java集合
  • leetcode388. Longest Absolute File Path
  • text-decoration与color属性
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 前端技术周刊 2019-02-11 Serverless
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 使用docker-compose进行多节点部署
  • 数据仓库的几种建模方法
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 昨天1024程序员节,我故意写了个死循环~
  • #Datawhale X 李宏毅苹果书 AI夏令营#3.13.2局部极小值与鞍点批量和动量
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • $ git push -u origin master 推送到远程库出错
  • (02)Unity使用在线AI大模型(调用Python)
  • (2)STM32单片机上位机
  • (3) cmake编译多个cpp文件
  • (Java)【深基9.例1】选举学生会
  • (每日一问)操作系统:常见的 Linux 指令详解
  • (七)c52学习之旅-中断
  • (三十五)大数据实战——Superset可视化平台搭建
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (一)SvelteKit教程:hello world
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)ABI是什么
  • (转)编辑寄语:因为爱心,所以美丽
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET COER+CONSUL微服务项目在CENTOS环境下的部署实践
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .Net多线程总结
  • .NET开发不可不知、不可不用的辅助类(一)
  • .net开发时的诡异问题,button的onclick事件无效
  • ??eclipse的安装配置问题!??
  • @Async 异步注解使用
  • @GlobalLock注解作用与原理解析
  • @Import注解详解
  • [4]CUDA中的向量计算与并行通信模式