解密Spring Boot:深入理解条件装配与条件注解
文章目录
- 一、条件装配概述
- 1.1 条件装配的基本原理
- 1.2 条件装配的作用
- 二、常用注解
- 2.1 @ConditionalOnClass
- 2.2 @ConditionalOnBean
- 2.3 @ConditionalOnProperty
- 2.4 @ConditionalOnExpression
- 2.5 @ConditionalOnMissingBean
- 三、条件装配的实现原理
- 四、实际案例
一、条件装配概述
1.1 条件装配的基本原理
条件装配的基本原理是根据特定的条件来决定是否应用特定的配置或组件。在 Spring Boot 中,条件装配是通过条件注解来实现的。
条件注解是一种特殊的注解,用于标记在配置类、组件类或方法上。它们根据某些条件的结果来决定是否应用相应的配置或组件。
条件注解的基本原理:
- 条件判断:Spring 在处理配置类或组件时,会对标记了条件注解的类或方法进行条件判断。
- 条件匹配:条件注解中定义的条件匹配器会根据特定的条件,如类路径是否存在、Bean 是否存在、属性是否被设置等,对环境进行判断,如果条件满足则返回 true,否则返回 false。
- 条件注解处理器:Spring 容器会使用条件注解处理器来处理条件注解,根据条件匹配的结果来决定是否应用相应的配置或组件。
- 应用配置或组件:根据条件注解的处理结果,Spring 容器会决定是否应用相应的配置或组件。如果条件满足,则进行相应的配置或组件的注册和初始化;如果条件不满足,则忽略该配置或组件。
1.2 条件装配的作用
条件装配的作用在于根据特定的条件来决定是否应用特定的配置或组件,从而实现灵活性和可配置性。
条件装配实现的作用:
- 环境适配:通过条件装配,可以根据当前的运行环境(如开发环境、测试环境、生产环境)或者配置(如不同的数据库、不同的服务提供商)来动态地选择合适的配置或组件,从而使应用程序适应不同的环境。
- 可插拔性:条件装配可以根据应用程序的需求动态地选择性地应用不同的配置或组件,使得应用程序的功能可以根据需求进行扩展或者替换,从而增强了应用程序的可插拔性和可扩展性。
- 简化配置:通过条件装配,可以根据特定的条件自动地应用相应的配置或组件,而无需手动配置或编写复杂的条件判断逻辑,从而简化了配置过程,提高了配置的易用性和可维护性。
- 优化性能:通过条件装配,可以根据特定的条件选择性地应用相应的配置或组件,避免不必要的资源消耗,从而优化了应用程序的性能和资源利用率。
二、常用注解
2.1 @ConditionalOnClass
@ConditionalOnClass
是 Spring Boot 中的一个条件注解,用于在类路径中存在指定的类时才会应用相应的配置。
定义了一个灵活的条件注解 ConditionalOnClass
,它可以根据类路径中特定类的存在与否来决定是否应用相应的配置或组件。
示例和用法说明:
/*** 只有当应用程序的类路径中存在 RedisTemplate 类时,RedisConfiguration 类中定义的 redisTemplate() 方法才会被注册为 Bean,并被 Spring 容器管理* 如果类路径中不存在 RedisTemplate 类,则该配置类中的 Bean 将被忽略*/
@Configuration
@ConditionalOnClass({org.springframework.data.redis.core.RedisTemplate.class})
public class RedisConfiguration {@Beanpublic RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();// 配置 RedisTemplate 的相关属性return redisTemplate;}
}
2.2 @ConditionalOnBean
@ConditionalOnBean
是 Spring Framework 中的一个条件注解,它的作用是在容器中存在指定的 Bean 时,才会应用相应的配置或组件。如果指定的 Bean 不存在,则该配置或组件将被忽略。
定义了一个具有多个属性的注解 ConditionalOnBean
,可以用于指定条件判断所依赖的类、名称、注解等信息,以及搜索依赖 Bean 的策略和泛型容器中的参数化类型。
示例和用法说明:
- 基本用法:
/*** MyService 类被标记为 @ConditionalOnBean(MyBean.class),这意味着只有当容器中存在 MyBean 类型的 Bean 时,MyService 才会被创建并添加到容器中*/
@Configuration
public class MyConfiguration {@Beanpublic MyBean myBean() {return new MyBean();}@ConditionalOnBean(MyBean.class)@Beanpublic MyService myService() {return new MyService();}
}
- 多个 Bean 的情况:
/*** MyService 类被标记为 @ConditionalOnBean({MyBean.class, AnotherBean.class}),这意味着只有当容器中同时存在 MyBean 和 AnotherBean 类型的 Bean 时,MyService 才会被创建并添加到容器中*/
@Configuration
public class MyConfiguration {@Beanpublic MyBean myBean() {return new MyBean();}@Beanpublic AnotherBean anotherBean() {return new AnotherBean();}@ConditionalOnBean({MyBean.class, AnotherBean.class})@Beanpublic MyService myService() {return new MyService();}
}
- 使用名称来指定 Bean:
/*** MyService 类被标记为 @ConditionalOnBean(name = {"myBean", "anotherBean"}),这意味着只有当容器中同时存在名称为 "myBean" 和 "anotherBean" 的 Bean 时,MyService 才会被创建并添加到容器中*/
@Configuration
public class MyConfiguration {@Bean(name = "myBean")public MyBean myBean() {return new MyBean();}@Bean(name = "anotherBean")public AnotherBean anotherBean() {return new AnotherBean();}@ConditionalOnBean(name = {"myBean", "anotherBean"})@Beanpublic MyService myService() {return new MyService();}
}
2.3 @ConditionalOnProperty
@ConditionalOnProperty
注解是 Spring Framework 中的条件注解之一,用于基于配置属性的存在与否来决定是否应用某个配置。
定义了一个具有多个属性的注解 ConditionalOnProperty
,它可以用于根据配置文件中的属性值来决定是否应用某个配置。
示例和说明:
/*** @ConditionalOnProperty 注解指定了一个名为myapp.feature.enabled 的属性,当这个属性存在并且其值为"true"时,MyFeatureConfiguration 配置类中的配置会生效* havingValue 参数指定了期望的属性值,如果没有指定havingValue,则默认匹配任何非空值* matchIfMissing 参数指定了当配置文件中未设置该属性时,是否应该匹配。如果设置为 true,则表示当属性不存在时也匹配,这样可以设置默认行为*/
@Configuration
@ConditionalOnProperty(name = "myapp.feature.enabled",havingValue = "true",matchIfMissing = true
)
public class MyFeatureConfiguration {}
myapp.feature.enabled=true
2.4 @ConditionalOnExpression
@ConditionalOnExpression
是 Spring 框架中的一个条件注解,在应用配置时根据 SpEL表达式的结果来决定是否进行配置。它允许我们使用更灵活的表达式来控制配置的条件。
定义了一个具有一个属性的注解 ConditionalOnExpression
,它可以根据 SpEL
表达式的结果来决定是否应用某个配置。
示例和说明:
/*** 检查配置文件中的 my.config.enabled 属性是否等于 'true'* 如果等于 'true',则表达式结果为 true`,MyBean 实例将会被创建* 否则,表达式结果为 false,配置将被忽略,不会创建 MyBean 实例*/
@Configuration
public class MyConfig {@Bean@ConditionalOnExpression("#{environment.getProperty('my.config.enabled') == 'true'}")public MyBean myBean() {// 配置生效时创建 MyBean 实例return new MyBean();}
}
2.5 @ConditionalOnMissingBean
@ConditionalOnMissingBean
是一个 Spring Boot 中常用的条件注解,它的作用是:当容器中不存在指定的 Bean 时,才会进行配置。
定义了一个具有多个属性的注解 ConditionalOnMissingBean
,用于根据存在或缺少特定类型的 bean 来决定是否应用某个配置。
示例和说明:
/*** 使用 @ConditionalOnMissingBean 注解来判断容器中是否已经存在了 MyService 类型的 Bean* 如果不存在,则创建一个 MyServiceImpl 实例并返回* 否则,不进行任何操作。*/
@Configuration
public class MyConfiguration {@Bean@ConditionalOnMissingBean(MyService.class)public MyService myService() {return new MyServiceImpl();}
}
三、条件装配的实现原理
条件装配的实现原理主要基于Spring的IoC容器和@Conditional注解。
在Spring的IoC容器中,BeanFactoryPostProcessor和BeanPostProcessor是两个核心的接口,它们允许我们在bean的创建和配置过程中添加额外的逻辑。(想要了解源码,读者可以查看我前面的博文)
条件装配的实现原理:
- @Conditional注解:这个注解可以标记在类、方法或注解上,用于指定在特定的条件满足时才创建和配置bean。
@Conditional
注解需要一个Class
类型的参数,这个参数需要实现Condition
接口。 - Condition接口:这是一个函数式接口,它定义了一个
matches(ConditionContext context, AnnotatedTypeMetadata metadata)
方法。- 这个方法返回一个boolean值,表示条件是否满足。如果返回true,则Spring容器会创建和配置相应的bean;如果返回false,则不会创建和配置。
- 两个参数提供了关于Spring容器和当前正在评估的bean的元数据信息。
- 自动配置:在Spring Boot中,条件装配被广泛应用于自动配置。
- Spring Boot会根据我们在
pom.xml
文件中引入的依赖,自动配置相应的bean。 - 这是通过一系列的
AutoConfiguration
类实现的,这些类上通常会使用@ConditionalOnClass
、@ConditionalOnMissingBean
等注解来指定条件。
- Spring Boot会根据我们在
四、实际案例
假设正在开发一个在线商城的 Spring Boot 应用程序,其中包含了用户管理和订单管理两个模块。现在,希望在用户注册时发送一封欢迎邮件,但是如果用户已经在系统中存在,则不发送邮件。
ps:使用条件注解
@ConditionalOnMissingBean
来实现这一定制化功能。
- 创建一个邮件服务接口
EmailService
和实现类WelcomeEmailService
。
/*** 邮件服务接口*/
public interface EmailService {void sendWelcomeEmail(String email);
}/*** 发送欢迎邮件*/
@Service
public class EmailServiceImpl implements EmailService {@Overridepublic void sendWelcomeEmail(String email) {// 发送欢迎邮件的逻辑System.out.println("Sending welcome email to: " + email);}
}
- 创建一个用户服务类
UserService
,在用户注册时调用邮件服务发送欢迎邮件。
public interface UserService {public void registerUser(String email);
}/*** 在用户注册时检查是否已经存在该用户,如果不存在则发送欢迎邮件*/
@Service
public class UserServiceImpl implements UserService {private final UserMapper userMapper;private final EmailService emailService;@Autowiredpublic UserServiceImpl(UserMapper userMapper, EmailService emailService) {this.userMapper = userMapper;this.emailService = emailService;}@Overridepublic void registerUser(String email) {if(!userMapper.existsByEmail(email)) {userMapper.save(email);emailService.sendWelcomeEmail(email);}else {throw new IllegalArgumentException("Email already exists");}}
}
- 创建一个
UserRepository
实现,它使用HashSet
来模拟存储用户信息。
/*** 不想使用数据库,直接使用HashSet来模拟存储用户信息的email* 使用一个HashSet来存储注册过的email,HashSet不允许存储重复的元素* @author LEK*/
@Repository
public class UserMapper {private final Set<String> registeredEmails = new HashSet<>();public boolean existsByEmail(String email) {return registeredEmails.contains(email);}public void save(String email) {if (Objects.nonNull(email) && !email.isEmpty()) {registeredEmails.add(email);}}
}
- 使用
@ConditionalOnMissingBean
注解来确保只有在容器中不存在EmailService
的实现类时才会注入WelcomeEmailService
。这样,如果用户在系统中已经存在,就不会发送欢迎邮件。
@Configuration
public class EmailConfig {/*** 邮件配置* */@Bean@ConditionalOnMissingBean(EmailService.class)public EmailServiceImpl email() {return new EmailServiceImpl();}
}
- 新建
UserServiceImplTest
测试类,由于是使用HashSet
来模拟运行,每次启动都是不存在的,然后手动一下。
@SpringBootTest
public class UserServiceImplTest {@Autowiredprivate UserService userService;@Testpublic void testRegisterExistingUser() {String existingEmail = "existing@example.com";userService.registerUser(existingEmail);// 注册已存在的用户,预期会抛出 IllegalArgumentExceptionuserService.registerUser(existingEmail);}
}
- 运行效果。
哪儿有勤奋,哪儿就有成功
相关文章:

解密Spring Boot:深入理解条件装配与条件注解
文章目录 一、条件装配概述1.1 条件装配的基本原理1.2 条件装配的作用 二、常用注解2.1 ConditionalOnClass2.2 ConditionalOnBean2.3 ConditionalOnProperty2.4 ConditionalOnExpression2.5 ConditionalOnMissingBean 三、条件装配的实现原理四、实际案例 一、条件装配概述 1…...

【数据结构与算法】使用数组实现栈:原理、步骤与应用
💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《数据结构与算法》 期待您的关注 目录 一、引言 🎄栈(Stack)是什么? …...
cell的复用机制和自定义cell
cell的复用机制和自定义cell UITableView 在学习cell之前,我们需要先了解UITableView。UITableView继承于UIScrollView,拥有两个两个相关协议 UITableViewDelegate和UITableViewDataSource,前者用于显示单元格,设置行高以及对单…...

Redis 双写一致原理篇
前言 我们都知道,redis一般的作用是顶在mysql前面做一个"带刀侍卫"的角色,可以缓解mysql的服务压力,但是我们如何保证数据库的数据和redis缓存中的数据的双写一致呢,我们这里先说一遍流程,然后以流程为切入点来谈谈redis和mysql的双写一致性是如何保证的吧 流程 首先…...

《软件定义安全》之四:什么是软件定义安全
第4章 什么是软件定义安全 1.软件定义安全的含义 1.1 软件定义安全的提出 虚拟化、云计算、软件定义架构的出现,对安全体系提出了新的挑战。如果要跟上网络演进的步伐和业务快速创新的速度,安全体系应该朝以下方向演变。 𝟭 安全机制软件…...

将AIRNet集成到yolov8中,实现端到端训练与推理
AIRNet是一个图像修复网络,支持对图像进行去雾、去雨、去噪声的修复。其基于对比的退化编码器(CBDE),将各种退化类型统一到同一嵌入空间;然后,基于退化引导恢复网络(DGRN)将嵌入空间修复为目标图像。可以将AIRNet的输出与yolov8进行端到端集成,实现部署上的简化。 本博…...

hcache缓存查看工具
1、hcache概述 hcache是基于pcstat的,pcstat可以查看某个文件是否被缓存和根据进程pid来查看都缓存了哪些文件。hcache在其基础上增加了查看整个操作系统Cache和根据使用Cache大小排序的特性。官网:https://github.com/silenceshell/hcache 2、hcache安装 2.1下载…...

Java 数据类型 -- Java 语言的 8 种基本数据类型、字符串与数组
大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 004 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进…...

kafka-生产者事务-数据传递语义事务介绍事务消息发送(SpringBoot整合Kafka)
文章目录 1、kafka数据传递语义2、kafka生产者事务3、事务消息发送3.1、application.yml配置3.2、创建生产者监听器3.3、创建生产者拦截器3.4、发送消息测试3.5、使用Java代码创建主题分区副本3.6、屏蔽 kafka debug 日志 logback.xml3.7、引入spring-kafka依赖3.8、控制台日志…...

免费!GPT-4o发布,实时语音视频丝滑交互
We’re announcing GPT-4o, our new flagship model that can reason across audio, vision, and text in real time. 5月14日凌晨,OpenAI召开了春季发布会,发布会上公布了新一代旗舰型生成式人工智能大模型【GPT-4o】,并表示该模型对所有免费…...
DevOps的原理及应用详解(四)
本系列文章简介: 在当今快速变化的商业环境中,企业对于软件交付的速度、质量和安全性要求日益提高。传统的软件开发和运维模式已经难以满足这些需求,因此,DevOps(Development和Operations的组合)应运而生,成为了解决这些问题的有效方法。 DevOps是一种强调软件开发人员(…...

关于选择,关于处事
一个人选择应该选择的是勇敢,选择不应该选择的是无奈。放弃,不该放弃的是懦夫,不放弃应该放弃的是睿智。所以,碰到事的时候要先静,先不管什么事,先静下来,先淡定,先从容。在生活里要…...

大话设计模式解读02-策略模式
本篇文章,来解读《大话设计模式》的第2章——策略模式。并通过Qt和C代码实现实例代码的功能。 1 策略模式 策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。 策略模式的特点&#…...

展会邀请 | 龙智即将亮相2024上海国际嵌入式展,带来安全合规、单一可信数据源、可追溯、高效协同的嵌入式开发解决方案
2024年6月12日至14日,备受全球嵌入式系统产业和社群瞩目的2024上海国际嵌入式展(embedded world china 2024)即将盛大开幕,龙智将携行业领先的嵌入式开发解决方案亮相 640展位 。 此次参展,龙智将全面展示专为嵌入式行…...

codeforce round951 div2
A guess the maximum 问题: 翻译一下就是求所有相邻元素中max - 1的最小值 代码: #include <iostream> #include <algorithm>using namespace std;const int N 5e4;int a[N]; int n;void solve() {cin >> n;int ans 0x3f3f3f3f;…...
arcgis开发记录
目录 文章目录 [toc]**arcgis JavaScript API安装**1. arcgisAPI下载地址:https://developers.arcgis.com/downloads/2. 4.4版本API:本地配置3. 3.18版本修改方法 **angular2中加载arcgis JS API**** arcgis加载图层 并显示图层上点的信息****使用图层上…...

RPA-UiBot6.0数据整理机器人—杂乱数据秒变报表
前言 友友们是否常常因为杂乱的数据而烦恼?数据分类、排序、筛选这些繁琐的任务是否占据了友友们的大部分时间?这篇博客将为友友们带来一个新的解决方案,让我们共同学习如何运用RPA数据整理机器人,实现杂乱数据的快速整理,为你的工作减负增效! 在这里,友友们将了…...

Application UI
本节包含关于如何用DevExpress控件模拟许多流行的应用程序ui的教程。 Windows 11 UI Windows 11和最新一代微软Office产品启发的UI。 Office Inspired UI Word、Excel、PowerPoint和Visio等微软Office应用程序启发的UI。 如何:手动构建Office风格的UI 本教程演示…...

关于 Redis 中集群
哨兵机制中总结到,它并不能解决存储容量不够的问题,但是集群能。 广义的集群:只要有多个机器,构成了分布式系统,都可以称之为一个“集群”,例如主从结构中的哨兵模式。 狭义的集群:redis 提供的…...

C++必修:探索C++的内存管理
✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C学习 贝蒂的主页:Betty’s blog 1. C/C的内存分布 我们首先来看一段代码及其相关问题 int globalVar 1; static…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...