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:…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...

认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...

WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...

对象回调初步研究
_OBJECT_TYPE结构分析 在介绍什么是对象回调前,首先要熟悉下结构 以我们上篇线程回调介绍过的导出的PsProcessType 结构为例,用_OBJECT_TYPE这个结构来解析它,0x80处就是今天要介绍的回调链表,但是先不着急,先把目光…...