【Spring Boot 原理分析】- 自动配置
【Spring Boot 原理分析】- 自动配置
Condition 注解
Condition 是 Spring 4.0 增加的条件判断功能,通过这个功能可以实现选择的创建 Bean 操作
👑 我们在使用 Spring 的时候,只需导入某个依赖的坐标,就可以直接通过 Autwired 注解,进行依赖注入,那我们有没有想过,我们的 Spring 是怎么知道,我们要将那个对象注入到容器中呢?
就比如 SpringBoot 是如何知道要创建的 RedisTemplate 的呢?
好,那我们就带着这个问题向下继续看。
- 导入坐标
<!--导入 data-redis 坐标 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.7.2</version>
</dependency>
- 获取 redisTemplate 对象
@SpringBootApplication
public class SpringBootDomeApplication {
public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(SpringBootDomeApplication.class, args);//获取容器中的 redisTemplate 对象Object redisTemplate = run.getBean("redisTemplate");System.out.println(redisTemplate);}
}
可以看到最终输出打印了容器中获取的 redisTemplate 对象。
而当我们将 spring-boot-start-data-redis 坐标从依赖中去除时,当我们再次执行,肯定会报出一个 NoSuchBeanDefinitionException 的异常,如下图所示。
💡问题就是,我们的 Spring 是怎么知道,我们到底有没有导入 redisTemlate 这个的起步依赖的呢?
我们通过一个案例,对上面的这种情况进行一个分析
📒 在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求如下:
- 导入 Jedis 坐标后,加载该 Bean ,没导入,则不加载
首先我们的创建一个 User 实体对象,然后添加一个 UserConfig 的配置类,让 Spring 启动的时候,自动将我们的 User 对象注入到我们的容器当中。
public class User {
}
@Configuration
public class UserConfig {@Beanpublic User user(){return new User();}
}
然后在启动类中修改 getBean 的参数,获取我们注入的 User 对象
@SpringBootApplication
public class SpringBootDomeApplication {
public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(SpringBootDomeApplication.class, args);//获取容器中的 redisTemplate 对象Object redisTemplate = run.getBean("user");System.out.println(redisTemplate);}
}
为了控制我们的 Spring 在创建注入 User 到我们的 Spring 容器中,我在 UserConfig 的配置类上加一个 Condition 的注解。
@Configuration
public class UserConfig {@Bean@Conditional()public User user(){return new User();}
}
我们进入这个注解,然后看看这个注解都做了什么事呢?
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {Class<? extends Condition>[] value();
}
可以看到改注解中有一个 Condition 的参数,进入 Condition 这个接口中,可以看到我们需要给该注解提供一个Condition 的类对象 ,还有一个返回值为 Boolean 类型的方法matches 。
/*** 确定条件是否匹配。* 参数:* context – 条件上下文 metadata – 正在检查的 class OR method 的元数据* 返回:* true 如果条件匹配并且可以注册组件,或者 false 否决注释组件的注册*/
@FunctionalInterface
public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
其实看到这里大概就已经明白了,官方解释原来就是说,如果 matches 返回的是一个 true 那么就注入到容器当中,负责就不注入。
所以我们得首先有一个用于实现 Condition 接口的实现类,并对该接口中的 matches 方法提供相应的实现,然后将该实现类文件对象,放置到我们需要加载的类上的 Condition 注解上。
public class ClassCondtion implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return false;}
}
@Configuration
public class UserConfig {@Bean@Conditional(ClassCondtion.class)public User user(){return new User();}
}
默认情况下返回的是 false ,也就是说默认情况下是对象是在 Spring 初始化的时候,不注入到容器中的。
所以我们现在再次运行查看控制台输出。可以看到控制台报出一个 Bean 找不到的错误
我们将上面的 ClassCondition 类做略微的修改,再看看运行的情况。
public class ClassCondtion implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {try {//获取是否导入了该类Class<?> aClass = Class.forName("org.springframework.data.redis.core.RedisTemplate");} catch (ClassNotFoundException e) {return false;}return true;}
}
我们将 pom 文件中的 spring-boot-starter-data-redis 取消,我们的 User 对象则无法成功的注入到容器当中。
所以通过上面的案例,其实我们就明白了,原来我们的 Spring 在进行启动的时候,会先通过 Condition 对象进行判断,如果注入的条件满足(坐标导入),则将对象注入到容器中,否则不进行注入。
Condition 注解的动态控制
⚠️ 上面的案例中有一个明显的问题,我们在 Condition 的实现类中是采用硬编码的方式,将包路径写在入了 实现类中。那有没有办法通过注解方式,自己指定要扫描的包呢?
其实是可以,这就需要我自定义注解,然后组合我们的 Condition注解,在自定义的注解中,定义一个用于存储路径的 value 变量,具体的实现步骤我们接着往下看。
- 自定义 ConditionOnClass 注解
@Target({ElementType.TYPE, ElementType.METHOD}) //注解可加的范围
@Retention(RetentionPolicy.RUNTIME) //注解生效时机
@Documented //生成 Java document 文档
@Conditional(ClassCondtion.class)
public @interface ConditionOnClass {//定义一个用于存储扫描的路径的变量String[] value();
}
- 通过参数获取注解中的属性值
package com.peggy.springbootdome.condtion;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
public class ClassCondtion implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {try {//获取注解注解中的所有的属性值Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());String[] value = (String[]) attributes.get("value");
for (String s : value) {//获取是否导入了该类Class<?> aClass = Class.forName(s);}
} catch (ClassNotFoundException e) {return false;}return true;}
}
- 在自定义的类中加入,自己定义的注解,并指定类路径测试
@Configuration
public class UserConfig {@Bean@ConditionOnClass("org.springframework.data.redis.core.RedisTemplate")public User user(){return new User();}
}
通过上面两个案例其实就解释了我们的 SpringBoot 是如何自动进行对象的选择装配的。
🪧 其实在我们的 SpringBoot 当中是提供了很多的 Condition 这样类似的注解的
我们可以到 spring-boot-autconfig 该包查看。
相关文章:

【Spring Boot 原理分析】- 自动配置
【Spring Boot 原理分析】- 自动配置 Condition 注解 Condition 是 Spring 4.0 增加的条件判断功能,通过这个功能可以实现选择的创建 Bean 操作 👑 我们在使用 Spring 的时候,只需导入某个依赖的坐标,就可以直接通过 Autwired 注…...
简明易懂的JVM理解
文章目录简明易懂的JVM和GC理解写在前面Java虚拟机(JVM)的组成基本介绍结构类加载子系统(ClassLoader SubSystem)介绍类加载过程类加载过程小结双亲委派模型(Parent-Delegation Model)简介优点Java9的类加载的委派关系变动双亲委派模型小结运行时数据区(Runtime Data Areas)介绍…...

新考纲下的PMP考试有多难?
PMP考试在6月25号考试结束后,在网上引起一片哗然,新考纲领域与考点的转变使得考试难度加大:PMP考试敏捷和混合内容比重大,考试难度加大很多;考题更加注重考生的知识应用能力,领域更宽; 接下来我…...
朗润国际期货:知名投行/大佬打Call记
知名投行/大佬打Call记 2023年知名投行/大佬看好哪些投资标的 中国股市 高盛(2023年1月):将上涨15% 花旗(2023年1月):上半年会成为投资两点 摩根大通(2022年11月):M…...

遗传算法及Python实现
0 建议学时 4学时 1 人工智能概述 2020中国人工智能产业年会在苏州召开,会上发布的《中国人工智能发展报告2020》显示,过去十年(2011-2020) ,中国人工智能专利申请量达389571件,占全球总量的74.7%,位居世界第一。 报…...

零基础 Ubuntu 20.04.01 下搭建51单片机开发环境[开源编译器SDCC]
原创首发于CSDN,转载请注明出处,谢谢! 文章目录为何会在Linux下开发单片机个人系统环境与所用开发板安装开源编译器 sdccSTC MCU ISP 闪存工具 stcgal 的安装单片机代码的编译与测试|编写主代码 main.c|使用 sdcc 编译…...

手摸手快速入门 正则表达式 (Vue源码中的使用)
vue2源码 在 vue2 源码的 src\compiler\parser\html-parser.js 文件中 里面有大量的正则表达式,如下图 可以看到非常的长,不是我说,就前几行,如果没有相关的 正则表达式 的工具,我可能就被劝退了😭 这里…...

TCP/IP网络协议族分成及其每层作用
1、可以分为应用层、传输层、网络层、链路层 2、各层的作用 应用层(可以想象成是快递打包过程) 决定了向用户提供应用服务时通信的活动,将要进行的操作或者数据进行一个打包。 传输层(可以理解为选择顺丰、圆通等快递公司) 提供数据传输的方…...
041、子序列类型问题(labuladong)
子序列类型问题 一、经典动态规划:编辑距离 基于labuladong的算法网站,经典动态规划:编辑距离; 总结: 一般来说涉及到两个字符串的问题,需要依赖上一次的各种操作,一般使用dp tableÿ…...

linux系统开机文段释义
第一段Version 2.01.1204. Copyright (C) 2010American Megatrends, Inc.Press <DEL> or <F2> to entersetup. Press <F7> for BBS POPUP Menu.设备上电,提示按DEL键或者F2键进入BIOS设置。按F8可以调出启动设备列表,可以选择性的启动…...

抽奖动画大转盘抽奖思路与做法
抽奖是各类营销活动中最常见的一种形式,本产品需求大致如下:转盘周围跑马灯交替闪烁,点击抽奖,大转盘旋转,调用接口获取抽奖结果,大转盘指针指向对应的奖品。高保如下图12.整体思路本需求要求跑马灯交替闪烁…...
Java实现 - 华为2016研发工程师编程题
文章目录删数字符集合数独删数 题目描述 有一个数组 a[N] 顺序存放 0 ~ N-1 ,要求每隔两个数删掉一个数,到末尾时循环至开头继续进行,求最后一个被删掉的数的原始下标位置。以 8 个数 (N7) 为例 :{ 0,1,2…...
nginx的七层负载均衡
文章目录一、负载均衡介绍二、nginx的配置文件三、实验过程总结一、负载均衡介绍 四层负载均衡 所谓四层负载均衡是指OSI七层模型中的传输层, 那么传输层Nginx已经支持TCP/IP的控制, 所以只需要对客户端的请求进行TCP/IP协议的包转发就可以实现负载, 那么他的好处是性能非常快,…...

信息加密技术
介绍信息加密 信息加密是实现数据保密性的手段。 信息加密(Encryption)是将明文信息转换为密文信息,使之在缺少特殊信息时不可读的过程。只有拥有解密方法的对象,经由解密过程,才能将密文还原为正常可读的内容。 现…...

RS485通信总线详解
RS485 总线详解 RS-485 是美国电子工业协会(EIA)在 1983 年批准了一个新的平衡传输标准(Balanced Transmission Standard)也称作差分,EIA 刚开始将 RS(Recommended Standard)做为标准的前缀&am…...

罗技LogitechFlow技术--惊艳的多电脑切换体验
作者:Eason_LYC 悲观者预言失败,十言九中。 乐观者创造奇迹,一次即可。 一个人的价值,在于他所拥有的。所以可以不学无术,但不能一无所有! 技术领域:WEB安全、网络攻防 关注WEB安全、网络攻防。…...

社招中级前端笔试面试题总结
HTTP世界全览 互联网上绝大部分资源都使用 HTTP 协议传输;浏览器是 HTTP 协议里的请求方,即 User Agent;服务器是 HTTP 协议里的应答方,常用的有 Apache 和 Nginx;CDN 位于浏览器和服务器之间,主要起到缓存…...
东南大学研究生上学期英语期末总结
写在前面 作者:夏日 博客地址:https://blog.csdn.net/zss192 本文为东南大学研究生英语上学期期末总结,内容为根据老师所发 PPT 总结得来 相关资料: 点我查看 题型说明 Module 1 International Conference 50% 题型范围&am…...

leaflet 删除所有的marker图层,保留其他图层(085)
第085个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet项目中清除所有的marker图层,保留其他图层,详情请参考源代码。这里面主要用到了(layer instanceof L.Marker ,注意 L.Marker中Marker首字母要大写。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行…...

双因素方差分析全流程
上篇文章讲述了“单因素方差分析全流程总结”,单因素方差分析只是考虑了一个自变量(定类)与一个因变量(定量)之间的关系,但是在实际问题研究中可能研究两个或者几个因素与因变量之间的关系,例如…...

超大规模芯片验证:基于AMD VP1902的S8-100原型验证系统实测性能翻倍
引言: 随着AI、HPC及超大规模芯片设计需求呈指数级增长原型验证平台已成为芯片设计流程中验证复杂架构、缩短迭代周期的核心工具。然而,传统原型验证系统受限于单芯片容量(通常<5000万门)、多芯片分割效率及系统级联能力&#…...
Nginx Stream 层连接数限流实战ngx_stream_limit_conn_module
1.为什么需要连接数限流? 数据库/Redis/MQ 连接耗资源:恶意脚本或误配可能瞬间占满连接池,拖垮后端。防御慢速攻击:层叠式限速(连接数+带宽)可阻挡「Slow Loris」之类的 TCP 低速洪水。公平接入…...

图上合成:用于大型语言模型持续预训练的知识合成数据生成
摘要 大型语言模型(LLM)已经取得了显著的成功,但仍然是数据效率低下,特别是当学习小型,专业语料库与有限的专有数据。现有的用于连续预训练的合成数据生成方法集中于文档内内容,而忽略了跨文档的知识关联&a…...
JeecgBoot低代码管理平台
一、一句话理解 JeecgBoot JeecgBoot 是一个基于 Java 技术栈(主要是 Spring Boot 和 Vue)的快速开发脚手架。它的核心理念是:通过代码生成器和一系列预置模块,极大地减少程序员在开发企业级后台管理系统时重复的、模板化的工作&…...

垂起固定翼无人机应用及技术分析
一、主要应用行业 1. 能源基础设施巡检 电力巡检:适用于超高压输电线路通道的快速巡查,实时回传数据提升智能运检效率。 油田管道监测:利用长航时特性(1.5-2小时)对大范围管道进行隐患排查,减少人力巡…...

固定ip和非固定ip的区别是什么?如何固定ip地址
在互联网中,我们常会接触到固定IP和非固定IP的概念。它们究竟有何不同?如何固定IP地址?让我们一起来探究这个问题。 一、固定IP和非固定IP的区别是什么 固定IP(静态IP)和非固定IP(动态IP)是两种…...

并发编程实战(生产者消费者模型)
在并发编程中使用生产者和消费者模式能够解决绝大多数的并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度。 生产者和消费者模式: 在线程的世界中生产者就是产生数据的线程,而消费者则是消费数据的线程。在多线程开…...
学习笔记(26):线性代数-张量的降维求和,简单示例
学习笔记(26):线性代数-张量的降维求和,简单示例 1.先理解 “轴(Axis)” 的含义 张量的 “轴” 可以理解为 维度的方向索引 。对于形状为 (2, 3, 4) 的张量,3 个轴的含义是: 轴 0(axis0&…...

echarts使用graph、lines实现拓扑,可以拖动增加effect效果
options.js // import React from react // import * as echarts from echartsimport ./index.lessexport const useEchartsOptionFun ({ nodeDataList, getNodeLinksDataList, getLinesCoordsFun }) > {const option {title: {text: 拓扑关系图,top: top,left: center,}…...

增量式网络爬虫通用模板
之前做过一个项目,他要求是只爬取新产生的或者已经更新的页面,避免重复爬取未变化的页面,从而节省资源和时间。这里我需要设计一个增量式网络爬虫的通用模板。可以继承该类并重写部分方法以实现特定的解析和数据处理逻辑。这样可以更好的节约…...