解密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…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...
Redis——Cluster配置
目录 分片 一、分片的本质与核心价值 二、分片实现方案对比 三、分片算法详解 1. 范围分片(顺序分片) 2. 哈希分片 3. 虚拟槽分片(Redis Cluster 方案) 四、Redis Cluster 分片实践要点 五、经典问题解析 C…...
CSS(2)
文章目录 Emmet语法快速生成HTML结构语法 Snipaste快速生成CSS样式语法快速格式化代码 快捷键(VScode)CSS 的复合选择器什么是复合选择器交集选择器后代选择器(重要)子选择器(重要)并集选择器(重要)**链接伪类选择器**focus伪类选…...
第四讲:类和对象(下)
1. 再探构造函数 • 之前我们实现构造函数时,初始化成员变量主要使⽤函数体内赋值,构造函数初始化还有⼀种⽅ 式,就是初始化列表,初始化列表的使⽤⽅式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成 员列表ÿ…...
AUTOSAR实战教程--开放式通用DoIP刷写工具OpenOTA开发计划
目录 软件概述 安装与运行 界面说明 3.1 功能区划分 3.2 状态显示 基本操作流程 4.1 DoIP连接配置 4.2 服务配置(刷写流程) 4.3 执行操作 4.4 保存配置 4.5 加载配置 功能详解 5.1 核心功能模块 诊断服务配置 通信设置 文件下载 工具功…...
