第八节 条件装配案例讲解

一、条件装配的作用是什么

条件装配是 Spring 框架中一个强大的特性,使得开发者能够创建更加灵活和可维护的应用程序。在 Spring Boot 中,这个特性被大量用于自动配置,极大地简化了基于 Spring 的应用开发。

二、条件装配注解

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>${version}</version>
</dependency>

下面以版本2.1.13.RELEASE为例子。 下面是包路径

org.springframework.boot.autoconfigure.condition.XXX

2.1 条件注解

2.2 条件注解说明

下面是相关注解的简单说明,标注颜色的可以重点关注

注解

作用

@ConditionalOnBean

当容器中存在指定的Bean时,满足条件。

@ConditionalOnMissingBean

当容器中不存在指定的Bean时,满足条件。

@ConditionalOnClass

当类路径上存在指定的类时,满足条件。

@ConditionalOnMissingClass

当类路径上缺少指定的类时,满足条件。

@ConditionalOnProperty

当存在指定的配置属性,并且属性值与给定的值相匹配时,满足条件。

@ConditionalOnResource

当指定的资源存在于类路径上时,满足条件。

@ConditionalOnExpression

基于SpEL表达式的结果,当表达式为true时,满足条件。

@ConditionalOnJava

当Java的版本匹配指定的条件时,满足条件。

@ConditionalOnWebApplication

当应用程序是一个Web应用时,满足条件。

@ConditionalOnNotWebApplication

当应用程序不是一个Web应用时,满足条件。

@ConditionalOnJndi

当指定的JNDI存在时,满足条件。

@ConditionalOnSingleCandidate

当容器中只存在一个指定的Bean,或者即使有多个也有一个被标记为首选时,满足条件。

前面5个可以重点关注,它是最常用的。在第三节中,我将分别针对上面的几个案例进行举例说明。

三、条件装配举例说明

用代码的方式来展示条件装配的作用,以及在实际生活中到底如何使用。

3.1 灵活性举例

场景描述:

假设我们正在开发一个多环境支持的应用程序,该应用在开发环境中使用基于内存的数据库(如 H2),而在生产环境中使用更稳定和持久的关系数据库(如 PostgreSQL)。

实现方式:

不使用条件装配时,我们可能需要在应用程序的不同版本中手动更改数据源配置,或者在代码中进行环境检查来决定使用哪个数据库。这样做会增加维护工作,也容易出错。

使用条件装配,我们可以利用 注解来根据环境自动配置不同的数据源:

@Configuration
@ConditionalOnProperty(name = "use-in-memory-db", havingValue = "true")
public class H2DatabaseConfig {// Configuration for H2 Database
}@Configuration
@ConditionalOnProperty(name = "use-in-memory-db", havingValue = "false")
public class PostgresDatabaseConfig {// Configuration for PostgreSQL Database
}

例子解释:

在这个例子中,我们有两个配置类,分别为 H2DatabaseConfig 和 PostgresDatabaseConfig。它们都使用了 @ConditionalOnProperty 注解来判断一个名为 use-in-memory-db 的属性的值。

  • 如果 use-in-memory-db 属性设置为 true,Spring Boot 会实例化 H2DatabaseConfig 类并使用 H2 数据库配置。
  • 如果 use-in-memory-db 属性设置为 false,Spring Boot 会实例化 PostgresDatabaseConfig 类并使用 PostgreSQL 数据库配置

提示:案例演示了 @ConditionalOnProperty 使用方式。

3.2 自动装配案例

根据配置,选择使用不通的缓存实现类。

简化实现

public interface CacheService {void put(String key, Object value);Object get(String key);
}

Redis 缓存服务实现:

@Configuration
@ConditionalOnClass(name = "redis.clients.jedis.Jedis")
public class RedisCacheConfig {@Beanpublic CacheService redisCacheService() {return new RedisCacheService();}// RedisCacheService 实现了 CacheService 接口// 使用 Redis 客户端进行具体的缓存操作
}

内存缓存服务实现:

@Configuration
@ConditionalOnMissingClass(value = "redis.clients.jedis.Jedis")
public class InMemoryCacheConfig {@Beanpublic CacheService inMemoryCacheService() {return new InMemoryCacheService();}// InMemoryCacheService 实现了 CacheService 接口// 使用 Java Map 进行缓存操作
}

在这个例子中,我们有两个配置类 RedisCacheConfig 和 InMemoryCacheConfig,它们都定义了一个用于条件装配的 CacheService Bean。

@ConditionalOnClass(name = "redis.clients.jedis.Jedis") 注解在 RedisCacheConfig 类上,表明如果类路径包含 Redis 的 Jedis 客户端类,则创建和注册 RedisCacheService 为 CacheService 的实现。

相对地,@ConditionalOnMissingClass(value = "redis.clients.jedis.Jedis") 注解在 InMemoryCacheConfig 类上,表明如果类路径不包含 Redis 的 Jedis 客户端类,则创建和注册 InMemoryCacheService 为 CacheService 的实现。

在这个条件装配的例子中,Spring Boot 应用会自动检查 Redis 客户端库是否存在,并基于检查结果来决定使用哪个缓存实现。这个过程完全自动化,无需开发者介入,也无需修改配置文件。如果你将 Redis 客户端库添加到项目的依赖中,Spring Boot 将自动配置使用 RedisCacheService;如果移除了相关依赖,则会回退到使用 InMemoryCacheService。

提示:案例演示了 @ConditionalOnClassConditionalOnClass的使用方式。

3.4 避免条件冲突举例

假设我们的应用程序中既可以使用JPA来访问数据库,也可以使用MyBatis。但是,我们希望这两个持久层框架不要同时生效,以免它们尝试操作同一个数据库实体时产生冲突

JPA配置类:

@Configuration
@ConditionalOnClass(name = "org.springframework.data.jpa.repository.JpaRepository")
public class JpaConfig {// 这里配置JPA相关的Bean,例如EntityManagerFactory, TransactionManager等
}

MyBatis配置类:

@Configuration
@ConditionalOnClass(name = "org.mybatis.spring.SqlSessionFactoryBean")
@ConditionalOnMissingBean(type = "org.springframework.data.jpa.repository.JpaRepository")
public class MyBatisConfig {// 这里配置MyBatis相关的Bean,例如SqlSessionFactory, DataSource等
}

例子解释:

@ConditionalOnClass 注解检查类路径上是否存在指定的类。在这个例子中,JpaConfig 类上的 @ConditionalOnClass 注解会检查JPA的 JpaRepository 是否存在于类路径上,如果存在,那么JPA的相关配置就会被注册。

MyBatisConfig 类上同时使用了 @ConditionalOnClass 和 @ConditionalOnMissingBean 注解。@ConditionalOnClass 注解会检查类路径上是否存在MyBatis的 SqlSessionFactoryBean 类;而 @ConditionalOnMissingBean 注解则确保只有在不存在 JpaRepository 类型的Bean时,MyBatis的配置才会生效

避免Bean冲突:

这种配置方法确保了当两种持久层技术的类都存在于类路径上时,只有JPA的配置会生效,因为 JpaConfig 没有额外的限制条件。而MyBatis的配置则只有在JPA不可用(即类路径上没有 JpaRepository)时才会生效,从而避免了同时注册JPA和MyBatis持久层可能引起的Bean冲突。

举例说明,实际案例。以 tomcat 容器为例子。

请思考这样一个场景,为什么,我们的 SpringBoot 项目,不再关注 tomcat、jetty 容器了呢;他们的加载装配是如何实现的呢?

当Spring Boot在类路径上发现tomcat-embedded.jar时,它会自动配置Tomcat作为应用程序的默认服务器。同样,如果它发现jetty-embedded.jar,而没有发现Tomcat,那么它将配置Jetty服务器。

以下是简化后的Tomcat和Jetty自动配置的源码示例,用于解释Spring Boot是如何使用条件注解来实现自动配置的。

Tomcat自动配置:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public class TomcatServletWebServerFactoryConfiguration {@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory() {return new TomcatServletWebServerFactory();}}

上面的配置类使用了@ConditionalOnClass注解,它意味着只有当Servlet、Tomcat和UpgradeProtocol这三个类都存在于类路径上时,该配置类才会被考虑。此外,@ConditionalOnMissingBean注解确保只有在当前上下文中不存在ServletWebServerFactory类型的Bean时,才会创建TomcatServletWebServerFactory的Bean。

3.5 提升性能

避免加载和初始化不需要的组件,可以提升应用程序的启动速度和运行时性能

Spring Boot 条件装配通过确保只有在需要时才创建和注册特定的 Beans,从而帮助提高应用程序的启动速度和运行时性能。这将避免浪费资源去加载和初始化那些在当前应用程序环境中不需要的组件。

下面是一个代码示例,它展示了如何使用 Spring Boot 条件装配来决定是否配置一个用于性能监控的组件。

场景描述:

假设我们的应用程序包含一个性能监控组件,这个组件在生产环境中是有用的,但在开发和测试环境中可能不需要它,因为它可能会降低应用的性能和增加额外的资源消耗.

@Configuration
@ConditionalOnProperty(name = "app.monitoring.enable", havingValue = "true")
public class PerformanceMonitoringConfig {@Beanpublic PerformanceMonitor performanceMonitor() {// 初始化性能监控组件return new PerformanceMonitor();}// 其它与性能监控相关的Beans...
}

在这个例子中,我们创建了一个配置类 PerformanceMonitoringConfig,它包含了创建 PerformanceMonitor Bean 的方法。类上的 @ConditionalOnProperty 注解告诉 Spring Boot,只有当 app.monitoring.enable 属性的值设置为 true 时,才创建 PerformanceMonitor Bean。

应用配置文件:

开发或测试环境 application-dev.properties 或 application-test.properties:

# 在开发和测试环境中,禁用性能监控
app.monitoring.enable=false

生产环境 :

# 在生产环境中,启用性能监控
app.monitoring.enable=true

提升性能的效果:

通过上述条件装配的方式,Spring Boot应用会根据配置文件中的属性值决定是否加载和初始化性能监控组件。这种机制确保了在开发和测试环境中,不会因为性能监控组件的存在而造成不必要的性能开销;而在生产环境中,性能监控组件才会被激活,为我们提供关键的性能数据。

这样的条件装配策略有助于在不同的环境中优化资源使用和应用性能,因为只有真正需要的组件才会被加载,从而加快了应用程序的启动时间,并在运行时提高了性能。

3.6 Profile(补充)

根据环境不同(开发环境和生产环境)来使用不同的邮件服务实现。在生产环境中,我们使用真实的邮件服务,而在开发环境中,我们使用一个模拟的邮件服务以避免发送真实的邮件。

public interface EmailService {void sendEmail(String to, String subject, String content);
}
  1. 定义邮件服务接口:

除去上面的注解外,在 Spring 中也提供了 Profile 来实现一定的条件装配。

生产环境的邮件服务实现:

@Profile("prod")
@Service
public class SmtpEmailService implements EmailService {// SMTP邮件服务相关的配置和方法public void sendEmail(String to, String subject, String content) {// 实际的邮件发送逻辑}
}

开发环境的邮件服务模拟实现:

@Profile("dev")
@Service
public class MockEmailService implements EmailService {// 模拟邮件服务的相关配置和方法public void sendEmail(String to, String subject, String content) {System.out.println("Mock email sent to " + to + " with subject " + subject);// 其他模拟操作}
}

在这个例子中,我们定义了一个邮件服务的接口 EmailService 和两个实现 SmtpEmailService 以及 MockEmailService。每个实现类上都使用了 Spring 的 @Profile 注解来指定它应该在哪个应用配置文件激活的情况下被注册。

  • @Profile("prod") 表示 SmtpEmailService 将仅在应用程序的 prod 配置文件激活时注册到 Spring 容器中。
  • @Profile("dev") 表示 MockEmailService 将仅在应用程序的 dev 配置文件激活时注册到 Spring 容器中。

在不同的环境中,通过设置 spring.profiles.active 属性,我们可以决定激活哪个配置文件。

3.7 更多场景

还可以实现模块化的加载,不仅仅是停留在单独的一个类上面。

Spring Boot的条件装配可以支持模块化开发,模块化是指将一个应用程序分解成一组可以独立开发、测试和部署的模块。条件装配允许开发者根据应用程序的不同部分是否存在或者某个条件是否满足来实现模块的动态加载与集成。

特别适合多云场景; 比如当前,有很多信创项目,完全可以使用条件装配来加载不通的实现类。

作者曾经,使用条件装配改造过登录模块。

  1. 使用扫描登录
  2. 使用密码登录
  3. 使用 OAUTH 登录

......

除去上面案例,还可以进行改造。比如 mq 的改造。不同环境使用不同的 mq 进行改造等。

  1. 信创环境使用 activimq
  2. 云上使用 rocketmq

3.....

还有数据库的适配,mysql、pg等。

四、本章小结

系统通过上面的几个案例,让你感受到了条件装配它的作用和好处。 方便以后在工作项目中如何合适地使用它们。

已同步发布到公众号:面汤放盐 第八节 条件装配案例讲解 (qq.com)

掘金账号:​​​​​​​第八节 条件装配案例讲解 - 掘金 (juejin.cn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/331120.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android-自定义三角形评分控件

效果图 序言 在移动应用开发中&#xff0c;显示数据的方式多种多样&#xff0c;直观的图形展示常常能带给用户更好的体验。本文将介绍如何使用Flutter创建一个自定义三角形纬度评分控件&#xff0c;该控件可以通过动画展示评分的变化&#xff0c;让应用界面更加生动。 实现思…

Vue3实战easypan(六):回收站+设置

一、回收站 src/views/recycle/Recycle.vue <template><!-- 上方两个按钮 --><div class"top"><el-button type"success" :disabled"selectFileIdList.length 0" click"revertBatch"><span class"ic…

[保姆式教程]使用目标检测模型YOLO V8 OBB进行旋转目标的检测:训练自己的数据集(基于卫星和无人机的农业大棚数据集)

最近需要做基于卫星和无人机的农业大棚的旋转目标检测&#xff0c;基于YOLO V8 OBB的原因是因为尝试的第二个模型就是YOLO V8&#xff0c;后面会基于YOLO V9模型做农业大棚的旋转目标检测。YOLO V9目前还不能进行旋转目标的检测&#xff0c;需要修改代码 PS:欢迎大家分享农业大…

Plotly库利用滑块创建数据可视化

使用了Plotly库来创建一个数据可视化图表&#xff0c;并使用滑块来控制显示哪些数据 import plotly.graph_objects as go from plotly.subplots import make_subplots# 示例数据 x [1, 2, 3, 4, 5] y1 [1, 2, 3, 4, 5] y2 [5, 4, 3, 2, 1] y3 [2, 3, 1, 5, 4]# 创建子图 f…

12306技术内幕

公司内部做的一次技术分享 文章目录 12306的成就12306系统特点12306系统难点解决思路产品角度技术角度余票库存的表如何设计&#xff1f; 抢票软件推荐巨人的肩膀 对于未公开的技术部分&#xff0c;只能结合已公开的信息&#xff0c;去做大胆的猜想。 本文提到的一些解决方案&…

【车载开发系列】Autosar中的VFB

【车载开发系列】Autosar中的VFB # 【车载开发系列】Autosar中的VFB 【车载开发系列】Autosar中的VFB一. 什么是VFB二. VFB的优点与缺点1&#xff09;VFB的缺点2&#xff09;VFB的好处 三. RTE与VFB之间关系四. 总线架构模式 一. 什么是VFB Virtual Functional Bus。它就是虚拟…

Python函数、类和方法

大家好&#xff0c;当涉及到编写可维护、可扩展且易于测试的代码时&#xff0c;Python提供了一些强大的工具和概念&#xff0c;其中包括函数、类和方法。这些是Python编程中的核心要素&#xff0c;可以帮助我们构建高效的测试框架和可靠的测试用例。 本文将探讨Python中的函数、…

Vue3实战笔记(43)—Vue3组合式API下封装可复用ECharts图表组件

文章目录 前言一、封装echart图标钩子二、使用步骤总结 前言 接上文&#xff0c;已经安装好了ECharts&#xff0c;开始封装组件方便使用。 一、封装echart图标钩子 首先应用我们之前学习的钩子方式&#xff0c;在hooks目录下创建一个名为 useECharts.js 的文件&#xff0c;用…

从零起航,Python编程全攻略

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、Python入门之旅 二、Python进阶之道 三、Python爬虫实战 四、Python数据分析利器 五…

linux系统——终止进程命令

linux进程&#xff0c;有所谓进程树的概念&#xff0c;在此之上&#xff0c;有父进程与子进程 pgrep进程名可以查看进程信息 同时&#xff0c;此命令也可以使用参数进行调节 关于kill有一系列命令参数 echo $?可以输出上次命令执行的情况

【Spring Boot】深度复盘在开发搜索引擎项目中重难点的整理,以及遇到的困难和总结

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;【Spring Boot】深度复盘在开发搜索引擎项目中重难点的整理&#xff0c;以及遇到的困难和总结 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 什么是搜索引…

Ajax异步删除

在页面上定义一个按钮 <button type"button" class"btn"><a href"JavaScript:;" class"id" b_id"{{$attachment[id]}}">删除</a></button> js代码 <script>$(.id).click(function (){va…

[读论文]精读Self-Attentive Sequential Recommendation

论文链接&#xff1a;https://arxiv.org/abs/1808.09781 其他解读文章&#xff1a;https://mp.weixin.qq.com/s/cRQi3FBi9OMdO7imK2Y4Ew 摘要 顺序动态是许多现代推荐系统的一个关键特征&#xff0c;这些系统试图根据用户最近执行的操作来捕获用户活动的“上下文”。为了捕捉…

ES基础概念

本文不介绍如何使用ES&#xff08;使用ES见&#xff1a;&#xff09; 1.ES生态圈 ES&#xff1a; Logstash&#xff1a;数据处理服务程序&#xff0c;解析转换加工数据&#xff1b; Kibana&#xff1a;数据展示、集群管理&#xff0c;数据可视化、ES管理与监控、报表等&#xf…

区块链钱包如果丢失了私钥或助记词,资产还能恢复吗?

如果你丢失了区块链钱包的私钥或助记词&#xff08;通常是用于恢复钱包的短语或种子&#xff09;&#xff0c;那么你的资产在大多数情况下是无法恢复的。私钥是访问和控制你在区块链上资产的唯一凭证&#xff0c;而助记词&#xff08;如BIP39标准中的12、18、24个单词的短语&am…

【数据分析面试】53.推送消息的分布情况(SQL)

题目 我们有两个表&#xff0c;一个是 notification_deliveries 表&#xff0c;另一个是包含 created 和购买 conversion dates 的 users 表。如果用户没有购买&#xff0c;那么 conversion_date 列为 NULL。 编写一个查询&#xff0c;以获取用户转换前的推送通知总数的分布情…

无人机监测系统:天空之眼,精准掌握地球脉动

在当今信息化快速发展的时代&#xff0c;无人机技术以其独特的优势&#xff0c;正在成为资源调查、环境监测和规划支持的重要工具。无人机监测系统通过搭载多种传感器和设备&#xff0c;能够快速、高效地获取地表信息&#xff0c;为决策提供科学依据。 项目背景 随着全球环境…

SpringMVC接收请求参数的方式:

接收简单变量的请求参数 直接使用简单变量作为形参进行接收&#xff08;这里简单变量名称需要与接收的参数名称保持一致&#xff0c;否则需要加上RequestParam注解&#xff09;&#xff1a; 细节&#xff1a; 1&#xff1a;SpringMVC会针对常见类型&#xff08;八种基本类型及…

二叉排序树的创建

二叉排序树就是节点经过排序构建起的二叉树&#xff0c;其有以下性质&#xff1a; 1. 若它的左子树不为空&#xff0c;则左子树上所有节点的值均小于它的根节点的值。 2. 若它的右子树不为空&#xff0c;则右子树上所有节点的值均大于它的根节点的值。 3. 它的左、右子树也分…

python期末作业:批量爬取站长之家的网站排行榜数据并保存,数据分析可视化

爬虫作业,含python爬取数据和保存文件,数据分析使用pyecharts做数据可视化 整体上分析网站的排名,直观看各个网站的热度。 数据分析之后大致的效果: 整个项目分为两个大的部分,第一部分就是抓取网站排名数据,然后保存为Excel、csv等格式,其次就是从文件中…