SpringBoot源码解读与原理分析(三)条件装配

文章目录

    • 2.3 Spring Framework的条件装配
      • 2.3.1 基于Profile的装配
        • 1.Profile源码解读
        • 2.使用@Profile注解
        • (3)命令行参数配置Profile
        • 3.Profile运用于实际开发
        • 4.Profile的不足
      • 2.3.2 基于Conditional的装配
        • 1.@Conditional源码解读
        • 2.@Conditional使用
        • 3.ConditionalOnXXX系列注解

2.3 Spring Framework的条件装配

在实际开发中我们可能遇到以下场景:测试环境用8080端口,生产环境用9999端口;测试环境需要将某个组件注册到IOC容器,但生产环境又不需要。
为了解决在不同场景/条件/环境下满足不同组件的装配,Spring Framework提供了两种条件装配的方式:基于Profile和基于Conditional。

2.3.1 基于Profile的装配

1.Profile源码解读

If a {@code @Configuration} class is marked with {@code @Profile}, all of the {@code @Bean} methods and {@link Import @Import} annotations associated with that class will be bypassed unless one or more of the specified profiles are active.

如果一个标注了@Configuration的配置类被标注为@Profile,那么与该类关联的所有@Bean方法和@Import}注释将被绕过,除非一个或多个指定的配置文件处于活动状态。

A profile is a named logical grouping that may be activated programmatically via {@link ConfigurableEnvironment#setActiveProfiles} or declaratively by setting the {@link AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME spring.profiles.active} property as a JVM system property, as an environment variable, or as a Servlet context parameter in {@code web.xml} for web applications.

这里描述激活Profile的三种方式:JVM启动参数、环境变量、web.xml配置

简单概括,Profile提供了一种“基于环境的配置”,根据当前项目的不同运行时环境,可以动态地注册与当前运行环境匹配的组件。

2.使用@Profile注解

(1)BartenderConfiguration类添加@Profile注解

public class Bartender {private String name;public Bartender(String name) {this.name = name;}// gettter setter
}
@Configuration
@Profile("city")
public class BartenderConfiguration {@Beanpublic Bartender zhangsan() {return new Bartender("张三");}@Beanpublic Bartender lisi() {return new Bartender("李四");}}

(2)编程式配置Profile

public class TavernProfileApplication {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BartenderConfiguration.class);Stream.of(ctx.getBeanDefinitionNames()).forEach(System.out::println);}}

执行结果(已省略一些内部组件打印):

=========分割线=========
=========分割线=========

控制台没有打印zhangsan和lisi。

因为在默认情况下,ApplicationContext中的Profile为“default”,与配置的@Profile(“city”)不匹配,所以BartenderConfiguration不会生效,@Bean也就不会注册到IOC容器中。

要想zhangsan和lisi注册到IOC容器中,则需要给ApplicationContext设置一下激活的Profile。

public class TavernProfileApplication {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();ctx.getEnvironment().setActiveProfiles("city");ctx.register(BartenderConfiguration.class);ctx.refresh();System.out.println("=========分割线=========");Stream.of(ctx.getBeanDefinitionNames()).forEach(System.out::println);System.out.println("=========分割线=========");}}

执行结果(已省略一些内部组件打印):

=========分割线=========
bartenderConfiguration
zhangsan
lisi
=========分割线=========

zhangsan和lisi已注册到IOC容器。

注意:这里AnnotationConfigApplicationContext在创建对象时,没有传入配置类,则内部不会执行初始化逻辑,而是等到手动调用其refresh方法后才会初始化IOC容器(如果传入了会立即初始化IOC容器),在初始化过程中,一并处理环境配置。

(3)命令行参数配置Profile

上面使用的编程式配置Profile存在硬编码问题,如果需要切换Profile,则需要修改代码并重新编译。为此,SpringFramework还支持命令行参数配置Profile。

在IDEA中配置启动选项:
在IDEA中配置启动选项
在main方法中改回原来的构造方法传入配置类的形式:

public class TavernProfileApplication {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BartenderConfiguration.class);Stream.of(ctx.getBeanDefinitionNames()).forEach(System.out::println);}}

执行结果(已省略一些内部组件打印):

=========分割线=========
bartenderConfiguration
zhangsan
lisi
=========分割线=========

zhangsan和lisi成功注册到IOC容器。

3.Profile运用于实际开发

application.properties文件可以通过加profile后缀来区分不同环境下的配置文件(application-dev.properties、application-test.properties、application-prod.properties)

# application-dev.properties
server.port=8787# application-prod.properties
server.port=8989# application.properties
spring.profiles.active=dev #激活dev的配置
4.Profile的不足

Profile控制的是整个项目的运行环境,无法根据单个Bean的因素决定是否装配。这种情况要用第二种条件装配的方式:基于@Conditional注解。

2.3.2 基于Conditional的装配

Conditional,意为条件,可以使Bean的装配基于一些指定的条件。
换句话说,被标注@Conditional注解的Bean要注册到IOC容器时,必须满足@Conditional上指定的所有条件才允许注册。

1.@Conditional源码解读

The {@code @Conditional} annotation may be used in any of the following ways:

  • as a type-level annotation on any class directly or indirectly annotated with {@code @Component}, including {@link Configuration @Configuration} classes
  • as a meta-annotation, for the purpose of composing custom stereotype annotations
  • as a method-level annotation on any {@link Bean @Bean} method

@Conditional的三种使用方式:

  • 在任何直接或间接用@Component标注的类上作为类级别注,包括@Configuration类
  • 作为元注解,用于组合自定义构造型注解
  • 作为任何@Bean方法上的方法级注解

If a {@code @Configuration} class is marked with {@code @Conditional}, all of the {@code @Bean} methods, {@link Import @Import} annotations, and {@link ComponentScan @ComponentScan} annotations associated with that class will be subject to the conditions.

如果一个@Configuration配置类标注了@Conditional,那么与之相关联的@Bean方法,@Import导入,@ComponentScan注解都将适用于这些条件。

Class<? extends Condition>[] value();

@Conditional注解需要传入一个Condition接口实现类数组,说明在使用时还需要定义一个条件判断类作为匹配依据,实现Condition接口。

2.@Conditional使用

(1)创建判断Boss是否存在的条件判断类

public class Boss {
}
public class BossExistCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 使用BeanDefinition而不是Bean做判断,这是因为考虑到:// 当进行匹配时Boss对象可能尚未创建,使用BeanDefinition// 可以确保不会出现偏差return context.getBeanFactory().containsBeanDefinition(Boss.class.getName());}}

(2)吧台配置类使用@Conditional,并传入BossExistCondition

public class Bar {
}
@Configuration
public class BarConfiguration {@Bean@Conditional(BossExistCondition.class)public Bar bbBar() {return new Bar();}}

(3)测试

场景一:@EnableTavern只导入BarConfiguration,不导入Boss

@Documented
@Retention(RetentionPolicy.RUNTIME) //
@Target(ElementType.TYPE) // 该注解只能标注到类上
@Import({BarConfiguration.class})
public @interface EnableTavern {}

执行结果(已省略一些内部组件打印):

=========分割线=========
tavernConfiguration
com.star.springboot.conditional.BarConfiguration
=========分割线=========

Boss和bbBar均没有注册到IOC容器中。

场景二:@EnableTavern导入BarConfiguration和Boss

@Documented
@Retention(RetentionPolicy.RUNTIME) //
@Target(ElementType.TYPE) // 该注解只能标注到类上
@Import({Boss.class, BarConfiguration.class})
public @interface EnableTavern {}

执行结果(已省略一些内部组件打印):

=========分割线=========
tavernConfiguration
com.star.springboot.ioc.Boss
com.star.springboot.conditional.BarConfiguration
bbBar
=========分割线=========
Boss和bbBar均注册到IOC容器中,说明@Conditional已经起了作用。
3.ConditionalOnXXX系列注解

SpringBoot针对@Conditional注解扩展了一系列条件注解。

  • @ConditionalOnClass & @ConditionalOnMissingClass :检查当前项目的类路径下是否包含/缺少指定类。
  • @ConditionalOnBean & @ConditionalOnMissingBean :检查当前容器中是否注册/缺少指定Bean。
  • @ConditionalOnProperty :检查当前应用的属性配置。
  • @ConditionalOnWebApplication & @ConditionalOnNotWebApplication :检查当前应用是否为Web应用。
  • @ConditionalOnExpression :根据指定的SqEL表达式确定条件是否满足。

注意,@ConditionalOnXXX注解通常都用在自动配置类中,对于普通的配置类最好避免使用,以免出现判断偏差。

本节完,更多内容请查阅分类专栏:SpringBoot源码解读与原理分析

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

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

相关文章

[pdf]《软件方法》强化自测题业务建模需求分析共191页,230题

潘加宇《软件方法》强化自测题业务建模需求分析共191页&#xff0c;230题&#xff0c;已上传CSDN资源。 在完成书中自测题基础上&#xff0c;进一步强化。 也可到以下地址下载&#xff1a; 资料http://www.umlchina.com/url/quizad.html 如果需要网盘提取码&#xff1a;uml…

北斗卫星助力无人机在沙漠播种,促进沙漠治理

北斗卫星助力无人机在沙漠播种&#xff0c;促进沙漠治理 近年来&#xff0c;随着科技的不断发展&#xff0c;北斗卫星和无人机技术的结合被广泛应用于沙漠治理领域&#xff0c;为解决沙漠化问题提供了全新的思路和解决方案。 近日&#xff0c;黄河“几字弯”北岸的内蒙古自治…

FreeRTOS操作系统学习——空闲任务及其钩子函数

空闲任务 当 FreeRTOS 的调度器启动以后就会自动的创建一个空闲任务&#xff0c;这样就可以确保至少有一任务可以运行。但是这个空闲任务使用最低优先级&#xff0c;如果应用中有其他高优先级任务处于就绪态的话这个空闲任务就不会跟高优先级的任务抢占 CPU 资源。空闲任务还有…

mirthConnect忽略HTTPS SSL验证

mirthConnect SSL忽略验证 1、下载https网站证书 点击不安全---->证书无效 2、查看mirth 秘钥库口令 在mirthConnect 的conf目录下面keystore.storepass 3、导入证书到本地 在jdk的bin目录下面执行 keytool -importcert -file "下载的网站证书路径" -keysto…

经典语义分割(二)医学图像分割模型UNet

经典语义分割(二)医学图像分割模型UNet 我们之前介绍了全卷积神经网络( FCN) &#xff0c;FCN是基于深度学习的语义分割算法的开山之作。 今天我们介绍另一个语义分割的经典模型—UNet&#xff0c;它兼具轻量化与高性能&#xff0c;通常作为语义分割任务的基线测试模型&#x…

Springboot 的几种配置文件形式

方式一&#xff1a;多个yml文件 步骤1&#xff1a;创建多个配置文件 application.yml #主配置文件 application-dev.yml #开发环境的配置 application-prod.yml #生产环境的配置 application-test.yml #测试环境的配置步骤2&#xff1a;applicaiton.yml中指定配置 在a…

时光机关:探秘Java中的Timer和TimerTask

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 时光机关&#xff1a;探秘Java中的Timer和TimerTask 前言Timer和TimerTask的基本概念Timer&#xff1a;TimerTask&#xff1a;为何它们是 Java 中任务调度的得力工具&#xff1a; Timer的使用方法创建…

2024【问题解决】Github 2024无法克隆git clone自从签了2F2安全协议之后

项目场景:ping通Github但没法clone–502 问题描述 提示:ping通Github但没法clone--502: 例如:git clone https://gitclone.com/l.git/*** $ git clone https://github.com/darrenpig/Yocto Cloning into Yocto_tutorial... fatal: unable to access https://gitclone.co…

Yii2中如何使用scenario场景,使rules按不同运用进行字段验证

Yii2中如何使用scenario场景&#xff0c;使rules按不同运用进行字段验证 当创建news新闻form表单时&#xff1a; 添加新闻的时候执行create动作。 必填字段&#xff1a;title-标题&#xff0c;picture-图片&#xff0c;description-描述。 这时候在model里News.php下rules规则…

web3时事粥报

比特币正成为更具有吸引力的通胀对冲工具 在通胀的宏观经济浪潮中&#xff0c;比特币正逐渐崭露头角&#xff0c;成为那些渴望多元化投资组合的投资者眼中的璀璨明星。Kooner 预测&#xff0c;2024年&#xff0c;各种宏观经济挑战可能进一步提升比特币、黄金和白银等资产的避险…

shell脚本一键部署docker

Docker介绍 Docker 是一个开源的平台&#xff0c;用于开发、交付和运行应用程序。它利用容器化技术&#xff0c;可以帮助开发人员更轻松地打包应用程序及其依赖项&#xff0c;并将其部署到任何环境中&#xff0c;无论是开发工作站、数据中心还是云中。以下是 Docker 的一些关键…

python中的文件操作2

文件遍历 在Python中&#xff0c;遍历文件通常指的是逐行读取文件中的内容。这种方式对于处理大型文件特别有用&#xff0c;因为它不需要一次性将整个文件加载到内存中。下面是几种常见的遍历文件内容的方法&#xff1a; 1. 使用with语句和for循环 这是最推荐的方式&#xf…

两天学会微服务网关Gateway-Gateway工作原理

锋哥原创的微服务网关Gateway视频教程&#xff1a; Gateway微服务网关视频教程&#xff08;无废话版&#xff09;_哔哩哔哩_bilibiliGateway微服务网关视频教程&#xff08;无废话版&#xff09;共计17条视频&#xff0c;包括&#xff1a;1_Gateway简介、2_Gateway工作原理、3…

ROS2学习(七) Foxy版本ros2替换中间件。

在ros2使用的过程中&#xff0c;一开始选用的foxy版本&#xff0c;后来发现&#xff0c;foxy版本的ros2有很多问题。一个是foxy版本已经停止维护了。另一个问题是这个版本有很多bug, 后续的版本在功能实现上做了很大的改动&#xff0c;甚至说进行了重写。修复的一些问题&#x…

【应用多元统计分析】--多元数据的描述和展示(R语言)

一元随机变量 我们用协方差来刻画两个变量的相关关系&#xff0c;这里指的是线性相关关系。 对于一元随机变量的可视化最简单的就是散点图&#xff0c;大致可以看出X和Y之间的相关关系。如果想更好的看X、Y之间的相关关系&#xff0c;可以画二维的散点图。 总结&#xff1a; 均…

Postman如何做接口测试?你居然还不知道

Postman如何做接口测试1&#xff1a;如何导入 swagger 接口文档 在使用 postman 做接口测试过程中&#xff0c;测试工程师会往界面中填入非常多的参数&#xff0c;包括 url 地址&#xff0c;请求方法&#xff0c;消息头和消息体等一系列数据&#xff0c;在请求参数比较多的情况…

[项目设计] 从零实现的高并发内存池(五)

&#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[高并发内存池] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 ​ 目录 8 使用定长内存池脱离new 9. 释放对象时不传大小 10.性能优化 10.1…

API协议设计的十种技术

文章目录 前言1.REST2. GraphQL3. gRPC (google Remote Procedure Calls)4.Webhook5. 服务端的事件发送——SSE(Servver - Sent Events )6. EDI(Electronic Data Interchange)7. 面向API 的事件驱动设计8. WebSocket9.简单对象访问协议(SOAP)10. Message Queuing Telemetry …

vue系列——vscode,node.js vue开发环境搭建

第一步安装node.js 推荐使用nvm进行node.js 的安装 nvm(Node.js version manager) 是一个命令行应用&#xff0c;可以协助您快速地 更新、安装、使用、卸载 本机的全局 node.js 版本。 可以去网上查找相关版本 我这里使用 nvm-setu… 链接:https://pan.baidu.com/s/1UEUtmzw5x…

饮料换购 刷题笔记

直接开个计数器mask 每当饮料现存数-1&#xff1b; cnt;且mask; 一旦mask达到3 饮料现存数 计数器清零3 代码 #include <iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int main(){ int n; …