当前位置: 首页 > news >正文

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为例:

img

Edit Configurations中找到vm.options 选项,把系统环境改为 Linux,如下:

img

然后重新启动测试,发现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

img

​ 在使用时,一般设置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包冲突

解决方案&#xff1a; 在你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 的一个子项目&#xff0c;可以对 Spring Boot 应用程序进行监控和管理&#xff0c;并对外提供了大量的端点&#xff0c;可以选择使用 HTTP 端点或 JMX 来管理和监控应用程序。 这篇文章主要介绍我们的应用程序中怎么加入actuator来对应用进…...

文举论金:黄金原油全面走势分析策略指导。

市场没有绝对&#xff0c;涨跌没有定势&#xff0c;所以&#xff0c;对市场行情的涨跌平衡判断就是你的制胜法宝。欲望&#xff01;有句意大利谚语&#xff1a;让金钱成为我们忠心耿耿的仆人&#xff0c;否则&#xff0c;它就会成为一个专横跋扈的主人。空头&#xff0c;多头都…...

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&#xff08;简称RHCOS&…...

Web应用开发 - 实训三 B Servlet基础

Web应用开发 - 实训三 B Servlet基础 前言&#xff1a; 零、前期准备准备工具创建项目导入 jar 包配置运行设置 一、实训第一部分第一张图第二张图第三张图 二、实训第二部分第一张图第二张图 前言&#xff1a; eclipse 是不可能用的&#xff0c;并不是说它界面丑&#xff0c;…...

Debian12安装 Docker

Docker中基本概念 镜像(Image) 镜像&#xff0c;从认识上简单的来说&#xff0c;就是面向对象中的类&#xff0c;相当于一个模板。从本质上来说&#xff0c;镜像相当于一个文件系统。Docker 镜像是一个特殊的文件系统&#xff0c;除了提供容器运行时所需的程序、库、资源、配…...

Elasticsearch:为具有许多 and/or 高频术语的 top-k 查询带来加速

作者&#xff1a;Adrien Grand Disjunctive queries&#xff08;term_1 OR term_2 OR ... OR term_n&#xff09;非常常用&#xff0c;因此在提高查询评估效率方面它们受到了广泛关注。 Apache Lucene 对于评估 disjunctive queries 有两个主要优化&#xff1a;一方面用于详尽评…...

【pythonflask-1】简单实现加减乘除输入界面

app.py import flask from flask import Flask, render_template, request # 计算精确的浮点结果&#xff0c;float加法也计算不出来 from decimal import Decimalapp Flask(__name__)app.route(/) def home():return render_template(index.html)app.route(/calculate, meth…...

基于协同过滤算法的旅游推荐系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...

遇见问题:使用mybaties向数据库中插入数据,idea显示插入成功,但是数据库中并没有数据变化?

遇见问题&#xff1a;使用mybaties向数据库中插入数据&#xff0c;idea显示插入成功&#xff0c;但是数据库中并没有数据变化? 可能的原因有几种&#xff1a; 没有提交事务&#xff1a;在使用 MyBatis 进行数据库操作时&#xff0c;需要手动提交事务。你可以在插入数据完成后…...

markdown学习笔记

markdown学习笔记 1.文字&#xff08;依靠HTML&#xff09; 1.1文字缩进-空格转义符 单字符空&#xff1a;&emsp; 半字符空&#xff1a;&ensp;1.2文字对齐 「居中&#xff1a;」<center> 居中 </center> or <p align"center"> 居中 …...

C++项目实战——基于多设计模式下的同步异步日志系统-⑧-日志落地类设计

文章目录 专栏导读抽象基类StdoutSink类设计FileSink类设计RollBySizeSink类设计日志落地工厂类设计日志落地类整理日志落地拓展测试RollByTimeSink类设计测试代码测试完整代码 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领…...

从零开始探索C语言(八)----指针

文章目录 1. 什么是指针&#xff1f;2. 如何使用指针&#xff1f;3. NULL 指针4. 指针的算术运算5. 指针数组6. 指向指针的指针7. 传递指针给函数8. 从函数返回指针 有人说&#xff0c;指针是C语言的灵魂&#xff0c;所以学习C语言&#xff0c;学习指针是很有必要的。 通过指针…...

SpringMVC 的三种异常处理方式详解

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

莫比乌斯召回系统介绍

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

使用ASM修改组件化 ARouter

工程目录图 1. apt生成的字节码文件 2. asm 生成的代码 请点击下面工程名称&#xff0c;跳转到代码的仓库页面&#xff0c;将工程 下载下来 Demo Code 里有详细的注释 代码&#xff1a;TestCompont...

第21章_瑞萨MCU零基础入门系列教程之事件链接控制器ELC

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…...

(二十八)大数据实战——Flume数据采集之kafka数据生产与消费集成案例

前言 本节内容我们主要介绍一下flume数据采集和kafka消息中间键的整合。通过flume监听nc端口的数据&#xff0c;将数据发送到kafka消息的first主题中&#xff0c;然后在通过flume消费kafka中的主题消息&#xff0c;将消费到的消息打印到控制台上。集成使用flume作为kafka的生产…...

vue3:22、vue-router的使用

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

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...

从零手写Java版本的LSM Tree (一):LSM Tree 概述

&#x1f525; 推荐一个高质量的Java LSM Tree开源项目&#xff01; https://github.com/brianxiadong/java-lsm-tree java-lsm-tree 是一个从零实现的Log-Structured Merge Tree&#xff0c;专为高并发写入场景设计。 核心亮点&#xff1a; ⚡ 极致性能&#xff1a;写入速度超…...

Qwen系列之Qwen3解读:最强开源模型的细节拆解

文章目录 1.1分钟快览2.模型架构2.1.Dense模型2.2.MoE模型 3.预训练阶段3.1.数据3.2.训练3.3.评估 4.后训练阶段S1: 长链思维冷启动S2: 推理强化学习S3: 思考模式融合S4: 通用强化学习 5.全家桶中的小模型训练评估评估数据集评估细节评估效果弱智评估和民间Arena 分析展望 如果…...