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

Spring Boot @Conditional注解

       在Spring Boot中,@Conditional 注解用于条件性地注册bean。这意味着它可以根据某些条件来决定是否应该创建一个特定的bean。这个注解可以放在配置类或方法上,并且它会根据提供的一组条件来判断是否应该实例化对应的组件。

       要使用 @Conditional注解时,需要实现 Condition 接口并重写 matches 方法。此方法将返回一个布尔值以指示条件是否匹配。如果条件为真,则创建bean;否则跳过该bean的创建。

以下是一个简单的例子,展示了如何使用自定义条件:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;public class MyCustomCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 在这里添加你的条件逻辑// 例如,检查系统属性、环境变量、已经存在的beans等return false; // 根据条件逻辑返回true或false}
}
  • ConditionContext:提供了对当前解析上下文的访问,包括:
    • Environment:可以用来获取环境变量、系统属性等。
    • BeanFactory:如果可用的话,可以通过它访问已经注册的bean。
    • ClassLoader:可以用来检查类路径上的类是否存在。
    • EvaluationContext:可以用来评估SpEL表达式。
  • AnnotatedTypeMetadata 提供了对带有注解的方法或类元数据的访问,例如注解属性值。

自定义条件类

假设我们有一个应用程序,它应该根据操作系统的不同来决定是否加载特定的bean。我们可以创建一个名为 OnWindowsCondition 的条件类:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;public class OnWindowsCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return "win".equals(context.getEnvironment().getProperty("os.name").toLowerCase().substring(0, 3));}
}

然后在配置类中使用:

@Configuration
public class MyConfig {@Bean@Conditional(OnWindowsCondition.class)public WindowsSpecificService windowsSpecificService() {return new WindowsSpecificServiceImpl();}
}

Spring Boot提供内置条件注解

@ConditionalOnProperty

当你希望基于配置文件中的属性是否存在或者具有特定值来创建bean时,可以使用 @ConditionalOnProperty 注解。例如:

@Configuration
public class MyConfig {@Bean@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true")public MyFeature myFeature() {return new MyFeature();}
}

在这个例子中,只有当配置文件中存在名为 my.feature.enabled 的属性且其值为 true 时,才会创建 MyFeature bean。

更多用法
  • prefix:指定属性名前缀。
  • name:指定属性名(可以是数组,表示多个属性)。
  • havingValue:指定属性必须具有的值,默认为空字符串。
  • matchIfMissing:如果未找到属性,则默认匹配与否,默认为 false

例如,你可以这样配置:

@Bean
@ConditionalOnProperty(prefix = "app", name = "feature.enabled", havingValue = "true", matchIfMissing = false)
public FeatureService featureService() {return new FeatureServiceImpl();
}

这将确保只有在 app.feature.enabled=true 时才会创建 FeatureService bean;如果没有设置该属性且 matchIfMissing=false,则不会创建。

@ConditionalOnClass 和 @ConditionalOnMissingClass

这两个注解用于检查类路径下是否存在或不存在某些类。这在集成第三方库时非常有用,因为你可以有条件地注册与这些库相关的bean。例如:

@Configuration
@ConditionalOnClass(name = "com.example.ExternalLibraryClass")
public class ExternalLibraryConfig {// ...
}

如果类路径中存在 ExternalLibraryClass 类,则会应用此配置。

@ConditionalOnBean 和 @ConditionalOnMissingBean

这些注解用于根据上下文中是否存在指定类型的bean来决定是否创建新的bean。这对于确保不会重复注册相同功能的bean非常有用。

@Bean
@ConditionalOnMissingBean(MyService.class)
public MyService myService() {return new MyServiceImpl();
}

这里的意思是:如果上下文中还没有类型为 MyService 的bean,则创建一个新的 MyServiceImpl 实例并注册为bean。

使用 SpEL 表达式的 @ConditionalOnExpression

可以通过 @ConditionalOnExpression 来编写复杂的条件表达式。例如,基于多个属性组合或者环境变量来决定是否创建bean。

@Bean
@ConditionalOnExpression("${spring.application.name:'default'} == 'myapp' && ${env:dev} == 'prod'")
public ProdSpecificBean prodSpecificBean() {return new ProdSpecificBean();
}

这段代码意味着只有当应用程序名称为 'myapp' 并且环境变量 env 设置为 'prod' 时,才会创建 ProdSpecificBean

使用场景

动态条件评估

有时你可能需要在应用启动后根据某些变化(如用户输入或外部服务的状态)来动态调整bean的行为。虽然 @Conditional 主要用于启动时的静态条件判断,但你可以通过结合其他机制(如事件监听器、定时任务等)来实现类似的效果。

@Configuration
public class DynamicConditionConfig {private final AtomicBoolean shouldCreateBean = new AtomicBoolean(false);@Bean@ConditionalOnProperty(name = "dynamic.bean.enabled", havingValue = "true")public MyDynamicBean myDynamicBean() {return () -> shouldCreateBean.get();}// 模拟外部触发更新条件状态的方法public void updateCondition(boolean value) {shouldCreateBean.set(value);}
}

在这个例子中,MyDynamicBean 的行为依赖于一个原子布尔变量 shouldCreateBean,该变量可以在运行时被更改,从而影响bean的行为。

条件化的AOP切面

你还可以将条件应用于AOP切面,以实现更加灵活的横切关注点管理。例如:

@Aspect
@ConditionalOnProperty(name = "app.logging.enabled", havingValue = "true")
public class LoggingAspect {@Around("execution(* com.example.service.*.*(..))")public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object proceed = joinPoint.proceed();long executionTime = System.currentTimeMillis() - start;System.out.println(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + " executed in " + executionTime + "ms");return proceed;}
}

这段代码表示只有当 app.logging.enabled=true 时才会激活日志记录切面。

使用 @Profile 和 @Conditional 结合

有时候,你可能想要结合 @Profile@Conditional 来创建更精细的条件逻辑。例如:

@Configuration
@Profile("dev")
public class DevConfig {@Bean@ConditionalOnProperty(name = "feature.x.enabled", havingValue = "true")public FeatureX featureX() {return new FeatureXImpl();}
}

这里的意思是:仅在开发环境(dev profile)并且 feature.x.enabled=true 时才创建 FeatureX bean。

条件化代理

对于那些需要延迟初始化或者懒加载的bean,可以考虑使用 @Scope("proxy")@Lazy 注解,结合 @Conditional 来实现条件化代理。

@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Lazy
@ConditionalOnProperty(name = "lazy.init.feature", havingValue = "true")
public LazyInitFeature lazyInitFeature() {return new LazyInitFeatureImpl();
}

这确保了只有在满足条件且首次访问 lazyInitFeature bean时才会实例化它。

调试技巧

使用 @PostConstruct 和 @PreDestroy 监控bean生命周期

为了更好地理解哪些bean被创建或销毁,可以在bean类中添加 @PostConstruct@PreDestroy 方法,并输出日志信息。

@Component
@ConditionalOnProperty(name = "feature.y.enabled", havingValue = "true")
public class FeatureY {@PostConstructpublic void init() {System.out.println("FeatureY initialized.");}@PreDestroypublic void destroy() {System.out.println("FeatureY destroyed.");}
}

这种方法有助于跟踪bean的生命周期,并确认条件是否按预期工作。

使用 spring.main.banner-mode=off 减少干扰

当你专注于调试条件逻辑时,关闭Spring Boot启动横幅可以帮助减少不必要的输出,使日志更加清晰。

spring.main.banner-mode=off

常见问题

环境属性未正确加载

如果发现条件注解没有按照预期工作,请检查是否正确加载了环境属性文件(如 application.propertiesapplication.yml)。确保这些文件位于正确的路径下,并且包含所需的属性定义。

类路径冲突

当遇到条件注解不起作用的问题时,类路径冲突是一个常见的原因。特别是当你使用 @ConditionalOnClass@ConditionalOnMissingClass 时,确保项目中不存在重复的依赖项。你可以使用Maven或Gradle命令来分析依赖树:

  • Maven:
    mvn dependency:tree
  • Gradle:
    gradle dependencies

条件逻辑错误

仔细审查你的条件逻辑,确保它们符合预期。可以通过单元测试验证每个条件的行为。例如:

@Test
void testFeatureYEnabled() {ApplicationContextRunner runner = new ApplicationContextRunner().withPropertyValues("feature.y.enabled=true");runner.run(context -> assertThat(context).hasSingleBean(FeatureY.class));
}@Test
void testFeatureYDisabled() {ApplicationContextRunner runner = new ApplicationContextRunner().withPropertyValues("feature.y.enabled=false");runner.run(context -> assertThat(context).doesNotHaveBean(FeatureY.class));
}

注意事项

  • 条件注解只适用于Spring的配置阶段,因此它们不能用于运行时决策。
  • 当使用 @Conditional 或其他条件注解时,请确保你的条件逻辑不会导致循环依赖或意外的行为。
  • 在编写条件逻辑时,考虑到性能影响,尽量使条件判断轻量级。

相关文章:

Spring Boot @Conditional注解

在Spring Boot中,Conditional 注解用于条件性地注册bean。这意味着它可以根据某些条件来决定是否应该创建一个特定的bean。这个注解可以放在配置类或方法上,并且它会根据提供的一组条件来判断是否应该实例化对应的组件。 要使用 Conditional注解时&#…...

jpeg文件学习

相关最全的一篇文章链接:https://www.cnblogs.com/wtysos11/p/14089482.html YUV基础知识 Y表示亮度分量:如果只显示Y的话,图像看起来会是一张黑白照。 U(Cb)表示色度分量:是照片蓝色部分去掉亮度&#x…...

c++基于过程

前言: 笔记基于C黑马程序员网课视频:黑马程序员匠心之作|C教程从0到1入门编程,学习编程不再难_哔哩哔哩_bilibili 在此发布笔记,只是为方便学习,不做其他用途,原作者为黑马程序员。 1. C基础 1.1 用Visual Studio写C程…...

FOC软件 STM32CubeMX 使用

1、安装-及相关软件版本 展示版本注意事项:keil MDK和STM32CubeMX版本至少要大于等于图中版本。 2、 Motor Profiler 5.2.0使用方法...

leetcode hot 100 全排列

46. 全排列 已解答 中等 相关标签 相关企业 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 class Solution(object): def permute(self, nums): """ :type nums: List[int] :rtype: List[List[int…...

使用qrcode.vue生成当前网页的二维码(H5)

使用npm&#xff1a; npm install qrcode.vue 使用yarn&#xff1a; yarn add qrcode.vue package.json&#xff1a; 实现&#xff1a; <template><div class"code"><qrcode-vue :value"currentUrl" :size"size" render-as&…...

0055. shell命令--useradd

目录 55. shell命令--useradd 功能说明 语法格式 选项说明 选项 退出值 相关文件 /etc/passwd /etc/shadow /etc/group /etc/gshadow /etc/skel/ /etc/login.defs /etc/default/useradd 实践操作 注意事项 55. shell命令--useradd 功能说明 useradd 命令是 Lin…...

blender中合并的模型,在threejs中显示多个mesh;blender多材质烘培成一个材质

描述&#xff1a;在blender中合并的模型导出为glb&#xff0c;在threejs中导入仍显示多个mesh&#xff0c;并不是统一的整体&#xff0c;导致需要整体高亮或者使用DragControls等不能统一控制。 原因&#xff1a;模型有多个材质&#xff0c;在blender中合并的时候&#xff0c;…...

vue 本地自测iframe通讯

使用 postMessage API 来实现跨窗口&#xff08;跨域&#xff09;的消息传递。postMessage 允许你安全地发送消息到其他窗口&#xff0c;包括嵌套的 iframe&#xff0c;而不需要担心同源策略的问题。 发送消息&#xff08;父应用&#xff09; 1. 父应用&#xff1a;发送消息给…...

C++:单例模式

创建自己的对象&#xff0c;同时确保对象的唯一性。 单例类只能有一个实例☞静态成员static☞静态成员 必须类外初始化 单例类必须自己创建自己的唯一实例 单例类必须给所有其他对象提供这一实例 静态成员类内部可以访问 构造函数私有化☞构造函数私有外部不能创建&#x…...

SOME/IP 协议详解——信息格式

文章目录 1. 头部格式1.1 消息 ID&#xff08;Message ID&#xff09;1.2 长度&#xff08;Length&#xff09;1.3 请求 ID&#xff08;Request ID&#xff09;1.4 协议版本&#xff08;Protocol Version&#xff09;&#xff1a;1.5 接口版本&#xff08;Interface Version&am…...

C# GDI+数码管数字控件

调用方法 int zhi 15;private void button1_Click(object sender, EventArgs e){if (zhi > 19){zhi 0;}lcdDisplayControl1.DisplayText zhi.ToString();} 运行效果 控件代码 using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using …...

在交叉编译中,常见的ELF(elf)到底是什么意思?

ELF 是 Executable and Linkable Format 的缩写&#xff0c;中文翻译为“可执行与可链接格式”。它是一种通用的文件格式&#xff0c;主要用于存储可执行文件、目标文件&#xff08;编译后的中间文件&#xff09;、动态库&#xff08;.so 文件&#xff09;以及内存转储文件&…...

Unity开发AR之Vuforia-MultiTarget笔记

前言 在增强现实(AR)技术蓬勃发展的今天,越来越多的开发者开始探索如何将AR应用于各种场景中。Vuforia作为一个领先的AR开发平台,为开发者提供了强大的工具和功能,使得创建AR体验变得更加简单和直观。本文将为您介绍Vuforia的基本概念、特点,以及如何配置和使用MultiTar…...

深入解析 Oracle 的聚合函数 ROLLUP

目录 深入解析 Oracle 的聚合函数 ROLLUP一、ROLLUP 函数概述二、ROLLUP 函数语法三、ROLLUP 实例详解&#xff08;一&#xff09;基础分组聚合&#xff08;二&#xff09;引入 ROLLUP 函数&#xff08;三&#xff09;ROLLUP 与 NULL 值&#xff08;四&#xff09;多列复杂分组…...

Wend看源码-Java-集合学习(List)

摘要 本篇文章深入探讨了基于JDK 21版本的Java.util包中提供的多样化集合类型。在Java中集合共分类为三种数据结构&#xff1a;List、Set和Queue。本文将详细阐述这些数据类型的各自实现&#xff0c;并按照线程安全性进行分类&#xff0c;分别介绍非线程安全与线程安全的实现方…...

【软件】教务系统成绩提交工具使用步骤

【软件】教务系统成绩提交工具使用步骤 零、快速开始 安装 与大多数软件一样&#xff0c;安装步骤很简单&#xff0c;一直点击“下一步”即可快速完成安装&#xff0c;安装完成后&#xff0c;在桌面会有一个软件图标&#xff0c;双击即可打开软件主界面。 导入成绩到Excel中…...

IPsec协议,网络安全的秘密

IPsec概述 IPsec是一组基于网络层的安全协议&#xff0c;是保护IP数据包在网络传输过程中保持安全、隐秘以及真实。通过对IP数据包进行一些加密、认证&#xff0c;来防止数据在传输过程中被窃取、篡改甚至伪造&#xff0c;IPsec在企业内部网络的通信、远程办公、云服务连接等场…...

浅谈下Spring MVC的执行流程

什么是Spring MVC Spring MVC是一个基于Java的Web框架&#xff0c;用于构建Web应用程序。 它是Spring Framework的一部分&#xff0c;它提供了模型-视图-控制器&#xff08;MVC&#xff09;架构。 支持RESTful风格的URL请求&#xff0c;易于与其他视图技术集成&#xff0c;如…...

khadas edge2安装ubuntu22.04与ubuntu20.04 docker镜像

khadas edge2安装ubuntu22.04与ubuntu20.04 docker镜像 一、资源准备1.1 镜像文件1.2 刷机工具1.3 ubuntu20.04 docker镜像&#xff08;具备demon无人机所需各种驱动&#xff09; 二、开始刷机&#xff08;安装ubuntu22.04系统&#xff09;2.1 进入刷机状态2.2 刷机 三、docker…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...