Spring框架中的@Conditional系列注解
目录
- 1 @Contidional 介绍
- 1.1 Condition 接口
- 1.2 Spring @Conditional注解实例
- 1.3 @Conditional 与@Profile 的对比
- 2 Spring boot 扩展
- 2.1 @ConditionalOnClass和@ConditionalOnMissingClass注解
- 2.2 @ConditionalOnBean 和@ConditionalOnMissingBean注解
- 2.3 @ConditionalOnProperty注解
1 @Contidional 介绍
Conditional 是由SpringFramework提供的一个注解,位于 org.springframework.context.annotation 包内,定义如下。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {Class<? extends Condition>[] value();}
SpringBoot 模块大量的使用@Conditional 注释,我们可以将Spring的@Conditional注解用于以下场景:
- 可以作为类级别的注解直接或者间接的与@Component相关联,包括@Configuration类;
- 可以作为元注解,用于自动编写构造性注解;
- 作为方法级别的注解,作用在任何@Bean方法上。
1.1 Condition 接口
我们需要一个类实现Spring提供的Condition接口,它会匹配@Conditional所符合的方法,然后我们可以使用我们在@Conditional注解中定义的类来检查。
public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
1.2 Spring @Conditional注解实例
作用在方法上
先来看一个简单一些的示例,我们假设有三个角色老师Teacher、学生Student和父母Parent,三种环境Linux、Windows和MacOSX,如果是Linux环境,就注册Teacher,如果是Windows环境就注册Parent,如果是Mac 环境就注册Student。代码示例如下:
- 首先创建Teacher和Student对象,没有任何的属性和方法,只是一个空类
//如果当前工程运行在Windows系统下,就注册Student
public class Student {}//如果当前工程运行在Linux系统下,就注册Teacher
public class Teacher {}// 如果是Mac OSX 系统,就注册Parent
public class Parent {}
- 创建一个LinuxCondition和一个WindowsCondition,LinuxCondition能够匹配Linux环境,WindowsCondition能够匹配Windows环境,MacOSX 系统匹配mac环境。
public class LinuxCondition implements Condition {public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 获取系统环境的属性String systemName = context.getEnvironment().getProperty("os.name");if(systemName.contains("Linux")){return true;}return false;}
}//自定义一个判断条件
public class WindowsCondition implements Condition {/** ConditionContext context: spring容器上下文环境* AnnotatedTypeMetadata metadata :@Conditional修饰类型信息*/public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {String systemName = context.getEnvironment().getProperty("os.name");if(systemName.contains("Windows")){return true;}return false;}}public class OsxCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {String property = context.getEnvironment().getProperty("os.name");if(property.equals("Mac OS X")){return true;}return false;}
}
- 下面来新建匹配注册环境,如果系统是Linux环境,就注册Teacher,如果系统是Windows,就注册Parent,如果是Mac 系统,就注册Student
@Configuration
public class AppConfig {@Conditional(OsxCondition.class)@Beanpublic Student student(){return new Student();}@Conditional(LinuxCondition.class)@Beanpublic Teacher teacher(){return new Teacher();}@Conditional(WindowsCondition.class)@Beanpublic Parent parent(){return new Parent();}
}
- 新建测试类进行测试
public class ConditionTest {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);String[] names = context.getBeanDefinitionNames();for(String name : names){System.out.println("name = " + name);}}
}
由输出可以看出,name = student 被输出到控制台,也就是说,我当前所用的系统环境是MacOSX环境,所以注册的是OSXCondition,也就是student的bean。
手动设置系统环境
也可以进行手动修改vm.options,把当前的系统环境变为Linux 或者Windows,以Idea为例:
在Edit Configurations
中找到vm.options 选项,把系统环境改为 Linux,如下:
然后重新启动测试,发现Teacher 被注入进来了,修改当前环境为Windows,观察Parent也被注入进来并输出了。
作用在类上
@Conditional 注解可以作用在类上,表示此类下面所有的bean满足条件后都可以进行注入,通常与@Configuration注解一起使用。
- 新建一个
AppClassConfig
,在类上标注@Conditional()注解,并配置相关bean,如下:
@Conditional(value = OsxCondition.class)
上文表示如果是OsxCondition.class 的话,就注册student、teacher、parent
- 测试类不用修改,直接用原测试类进行测试,发现student、 teacher、 parent 都被注册进来了
多个条件类
因为@Conditional注解的value 方法默认传递一个数组,所以可以接受多个condition,为了测试如下情况,
新建一个 TestCondition
类,如下:
// 单纯为了测试
public class TestCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 返回false,表示不匹配return false;}
}
修改一下AppClassConfig
@Conditional(value = {OsxCondition.class,TestCondition.class})
也就是给@Conditional 多加了一个参数 TestCondition.class
启动之前的测试类,发现上述的bean都没有注入,也就是说,只有在满足OsxCondition.class 和 TestCondition.class 都为true的情况下,才会注入对应的bean,修改TestCondition.class的matches方法的返回值为true
,重新观察返回结果,发现上述bean都被注入了。
1.3 @Conditional 与@Profile 的对比
@Spring3.0 也有一些和@Conditional 相似的注解,它们是Spring SPEL 表达式和Spring Profiles 注解 Spring4.0的@Conditional 注解要比@Profile 注解更加高级。@Profile 注解用来加载应用程序的环境。@Profile注解仅限于根据预定义属性编写条件检查。 @Conditional注释则没有此限制。
Spring中的@Profile 和 @Conditional 注解用来检查"If…then…else"的语义。然而,Spring4 @Conditional是@Profile 注解的更通用法。
- Spring 3中的 @Profiles仅用于编写基于Environment变量的条件检查。 配置文件可用于基于环境加载应用程序配置。
- Spring 4 @Conditional注解允许开发人员为条件检查定义用户定义的策略。 @Conditional可用于条件bean注册。
2 Spring boot 扩展
SpringBoot的spring-boot-autoconfigure模块也提供了Conditional系列的相关注解,这些注解能帮助开发者根据一定的条件去装载需要的Bean。
2.1 @ConditionalOnClass和@ConditionalOnMissingClass注解
当Spring加载的Bean被@ConditionOnClass注解标记时,类加载器会先去先找到指定的Class, 如果没有找到目标Class,那么被ConditionOnClass注解标记的类不会被Spring装载,相反ConditionalOnMissingBean是指如果没有找到目标Class, 那么就装载该类。
2.2 @ConditionalOnBean 和@ConditionalOnMissingBean注解
当Spring加载的Bean被@ConditionalOnBean注解标记时,接下来会先找到指定的Bean,如果没有找到目标Bean,那么被@ConditionalOnBean标记的类不会被Spring装载,相反ConditionalOnMissingBean是指如果没有Class, 那么就装载该Bean。
看一个例子, Dubbo与Springboot做自动装配时,先寻找BASE_PACKAGES_BEAN_NAME这个Bean, 如果Bean 不存在,那么serviceAnnotationBeanProcessor这个Bean不会被Spring 装载.
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@Configuration
@AutoConfigureAfter(DubboRelaxedBindingAutoConfiguration.class)
@EnableConfigurationProperties(DubboConfigurationProperties.class)
@EnableDubboConfig
public class DubboAutoConfiguration {/*** Creates {@link ServiceAnnotationPostProcessor} Bean* dubbo.scan.base-packages* @param packagesToScan the packages to scan* @return {@link ServiceAnnotationPostProcessor}*/@ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME)// 先找BASE_PACKAGES_BEAN_NAME 这个bean, 如果没有这个bean, 那么serviceAnnotationBeanProcessor不会被Spring装载。@ConditionalOnBean(name = BASE_PACKAGES_BEAN_NAME)@Beanpublic ServiceAnnotationPostProcessor serviceAnnotationBeanProcessor(@Qualifier(BASE_PACKAGES_BEAN_NAME)Set<String> packagesToScan) {return new ServiceAnnotationPostProcessor(packagesToScan);}}
使用@ConditionalOnMissingBean注解定义BASE_PACKAGES_BEAN_NAME这个Bean
/*** Dubbo Relaxed Binding Auto-{@link Configuration} for Spring Boot 2.0** @see DubboRelaxedBindingAutoConfiguration* @since 2.7.0*/
@Configuration
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder")
@AutoConfigureBefore(DubboRelaxedBindingAutoConfiguration.class)
public class DubboRelaxedBinding2AutoConfiguration {public PropertyResolver dubboScanBasePackagesPropertyResolver(ConfigurableEnvironment environment) {ConfigurableEnvironment propertyResolver = new AbstractEnvironment() {@Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {Map<String, Object> dubboScanProperties = getSubProperties(environment.getPropertySources(), DUBBO_SCAN_PREFIX);propertySources.addLast(new MapPropertySource("dubboScanProperties", dubboScanProperties));}};ConfigurationPropertySources.attach(propertyResolver);return propertyResolver;}/*** The bean is used to scan the packages of Dubbo Service classes* 如果没有就创建* @param environment {@link Environment} instance* @return non-null {@link Set}* @since 2.7.8*/@ConditionalOnMissingBean(name = BASE_PACKAGES_BEAN_NAME)@Bean(name = BASE_PACKAGES_BEAN_NAME)public Set<String> dubboBasePackages(ConfigurableEnvironment environment) {PropertyResolver propertyResolver = dubboScanBasePackagesPropertyResolver(environment);return propertyResolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet());}@ConditionalOnMissingBean(name = RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME, value = ConfigurationBeanBinder.class)@Bean(RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME)@Scope(scopeName = SCOPE_PROTOTYPE)public ConfigurationBeanBinder relaxedDubboConfigBinder() {return new BinderDubboConfigBinder();}}
2.3 @ConditionalOnProperty注解
该注解的作用是解析application.yml/application.properties 里的配置生成条件来生效,也是与@Configuration注解一起使用。
属性 | 功能 | 其他 |
---|---|---|
prefix | 读取配置里的前缀值为prefix的属性, 如果没有返回false | |
name | 读取属性配置里的Key值,如果配置了prefix,那么需要先拼接prefix然后匹配havingValue值 | |
havingValue | 匹配属性里的值 | |
matchIfMissing | 当未找到对应的配置时是否匹配,默认为false, 如果为true,没有找到配置,那么也匹配。 |
使用场景,例如在指定数据源时,指定datasource的type。例如包含如下配置使用Hikari数据源。
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
在使用时,一般设置matchIfMissing=false, 这样条件没有匹配上的话会Spring在扫描bean时会自动跳过该配置类。
也可以设定matchIfMissing=true,这种场景例如缓存,我们可以这样配置默认是开启缓存的。
@ConditionalOnProperty(name={cache.effect},marchIfMissing=true)public class CacheAutoConfiguration{// ...}
如果在application.properties里配置cache.effect=false, 那么该配置类就会跳过,这样配置就能使缓存不生效。
相关文章:

Spring框架中的@Conditional系列注解
目录 1 Contidional 介绍1.1 Condition 接口1.2 Spring Conditional注解实例1.3 Conditional 与Profile 的对比 2 Spring boot 扩展2.1 ConditionalOnClass和ConditionalOnMissingClass注解2.2 ConditionalOnBean 和ConditionalOnMissingBean注解2.3 ConditionalOnProperty注解…...
spring boot + minio 8.5.4 遇到 okhttp3包冲突
解决方案: 在你spring boot项目的根pom上指定okhttp3版本, <properties><okhttp3.version>4.8.1 </okhttp3.version></properties> 这样其他的模块引入minio就不会报错了 <dependencies><!--minio oss服务--><dependenc…...

springboot整合actuator、admin对应用程序进行监控
Spring Boot Actuator 是 Spring Boot 的一个子项目,可以对 Spring Boot 应用程序进行监控和管理,并对外提供了大量的端点,可以选择使用 HTTP 端点或 JMX 来管理和监控应用程序。 这篇文章主要介绍我们的应用程序中怎么加入actuator来对应用进…...
文举论金:黄金原油全面走势分析策略指导。
市场没有绝对,涨跌没有定势,所以,对市场行情的涨跌平衡判断就是你的制胜法宝。欲望!有句意大利谚语:让金钱成为我们忠心耿耿的仆人,否则,它就会成为一个专横跋扈的主人。空头,多头都…...

Fedora CoreOS 安装部署详解
《OpenShift 4.x HOL教程汇总》 Fedora CoreOS 的裸机安装方法_fedora coreos 安装-CSDN博客 OpenShift 4 - Fedora CoreOS (1) - 最简安装_fedora core 安装_dawnsky.liu的博客-CSDN博客 OpenShift 和 CoreOS 我们知道 Red Hat Enterprise Linux CoreOS(简称RHCOS&…...

Web应用开发 - 实训三 B Servlet基础
Web应用开发 - 实训三 B Servlet基础 前言: 零、前期准备准备工具创建项目导入 jar 包配置运行设置 一、实训第一部分第一张图第二张图第三张图 二、实训第二部分第一张图第二张图 前言: eclipse 是不可能用的,并不是说它界面丑,…...
Debian12安装 Docker
Docker中基本概念 镜像(Image) 镜像,从认识上简单的来说,就是面向对象中的类,相当于一个模板。从本质上来说,镜像相当于一个文件系统。Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配…...

Elasticsearch:为具有许多 and/or 高频术语的 top-k 查询带来加速
作者:Adrien Grand Disjunctive queries(term_1 OR term_2 OR ... OR term_n)非常常用,因此在提高查询评估效率方面它们受到了广泛关注。 Apache Lucene 对于评估 disjunctive queries 有两个主要优化:一方面用于详尽评…...

【pythonflask-1】简单实现加减乘除输入界面
app.py import flask from flask import Flask, render_template, request # 计算精确的浮点结果,float加法也计算不出来 from decimal import Decimalapp Flask(__name__)app.route(/) def home():return render_template(index.html)app.route(/calculate, meth…...

基于协同过滤算法的旅游推荐系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...
遇见问题:使用mybaties向数据库中插入数据,idea显示插入成功,但是数据库中并没有数据变化?
遇见问题:使用mybaties向数据库中插入数据,idea显示插入成功,但是数据库中并没有数据变化? 可能的原因有几种: 没有提交事务:在使用 MyBatis 进行数据库操作时,需要手动提交事务。你可以在插入数据完成后…...

markdown学习笔记
markdown学习笔记 1.文字(依靠HTML) 1.1文字缩进-空格转义符 单字符空:  半字符空: 1.2文字对齐 「居中:」<center> 居中 </center> or <p align"center"> 居中 …...

C++项目实战——基于多设计模式下的同步异步日志系统-⑧-日志落地类设计
文章目录 专栏导读抽象基类StdoutSink类设计FileSink类设计RollBySizeSink类设计日志落地工厂类设计日志落地类整理日志落地拓展测试RollByTimeSink类设计测试代码测试完整代码 专栏导读 🌸作者简介:花想云 ,在读本科生一枚,C/C领…...

从零开始探索C语言(八)----指针
文章目录 1. 什么是指针?2. 如何使用指针?3. NULL 指针4. 指针的算术运算5. 指针数组6. 指向指针的指针7. 传递指针给函数8. 从函数返回指针 有人说,指针是C语言的灵魂,所以学习C语言,学习指针是很有必要的。 通过指针…...

SpringMVC 的三种异常处理方式详解
目录 1. 什么是异常 2. 为什么要全局异常处理 3. SpringMVC异常分类 4. 异常处理思路 5. 三种异常处理方式示例 ① 配置 SimpleMappingExceptionResolver 处理器 ② 实现 HandlerExceptionResolver 接口 ③ 使用ControllerAdviceExceptionHandler实现全局异常 6. 响应…...

莫比乌斯召回系统介绍
当前召回系统只能召回相关性高的广告,但不能保证该广告变现能力强。莫比乌斯做了如下两点创新: 在召回阶段,引入CPM等业务指标作为召回依据在召回阶段,引入CTR模型,从而召回更多相关性高且变现能力强的广告 参考 百度…...

使用ASM修改组件化 ARouter
工程目录图 1. apt生成的字节码文件 2. asm 生成的代码 请点击下面工程名称,跳转到代码的仓库页面,将工程 下载下来 Demo Code 里有详细的注释 代码:TestCompont...

第21章_瑞萨MCU零基础入门系列教程之事件链接控制器ELC
本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id728461040949 配套资料获取:https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总: ht…...

(二十八)大数据实战——Flume数据采集之kafka数据生产与消费集成案例
前言 本节内容我们主要介绍一下flume数据采集和kafka消息中间键的整合。通过flume监听nc端口的数据,将数据发送到kafka消息的first主题中,然后在通过flume消费kafka中的主题消息,将消费到的消息打印到控制台上。集成使用flume作为kafka的生产…...

vue3:22、vue-router的使用
import { createRouter, createWebHistory } from vue-router//history模式:createWebHistory //hash模式:createWebHashHistory//vite中的环境变量 import.meta.env.BASE_URL 就是vite.config.js中的base配置项 const router createRouter({history:…...

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.…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...

论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...

2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...
stm32进入Infinite_Loop原因(因为有系统中断函数未自定义实现)
这是系统中断服务程序的默认处理汇编函数,如果我们没有定义实现某个中断函数,那么当stm32产生了该中断时,就会默认跑这里来了,所以我们打开了什么中断,一定要记得实现对应的系统中断函数,否则会进来一直循环…...

以太网PHY布局布线指南
1. 简介 对于以太网布局布线遵循以下准则很重要,因为这将有助于减少信号发射,最大程度地减少噪声,确保器件作用,最大程度地减少泄漏并提高信号质量。 2. PHY设计准则 2.1 DRC错误检查 首先检查DRC规则是否设置正确,然…...