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

news2024/11/24 6:44:11

文章目录

    • 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 {

    @Bean
    public Bartender zhangsan() {
        return new Bartender("张三");
    }

    @Bean
    public 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 {

    @Override
    public 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.coloradmin.cn/o/1493771.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kosmos-1: 通用接口架构下的多模态大语言模型

Kosmos-1: 通用接口架构下的多模态大语言模型 FesianXu 20230513 at Baidu Search Team 前言 在大规模语言模型&#xff08;Large Language Model, LLM&#xff09;看似要带来新一番人工智能变革浪潮之际&#xff0c;越来越多尝试以LLM作为通用接口去融入各种任务的工作&#…

WordPress建站入门教程:小皮面板phpstudy如何安装PHP和切换php版本?

小皮面板phpstudy支持的PHP版本有很多&#xff0c;包括5.2.17、5.3.29、5.4.45、5.5.9、5.6.9、7.0.9、7.1.9、7.2.9、7.3.4、7.3.9、7.4.3、8.0.2、8.2.9。那么我们如何安装其他的php版本和切换网站的php版本呢&#xff1f;只需要简单几步即可&#xff0c;具体如下&#xff1a…

JavaWeb - 2 - HTML、CSS

什么是HTML、CSS&#xff1f; HTML&#xff08;HyperText Markup Language&#xff09;&#xff1a;超文本标记语言 超文本&#xff1a;超越了文本的限制&#xff0c;比普通文本更强大&#xff0c;除了文字信息&#xff0c;还可以定义图片、音频、视频等内容 标记语言&…

不用下载的工具却能保存西瓜视频的原画视频,支持无水印!

近年来&#xff0c;西瓜视频可谓是炙手可热&#xff0c;得益于其强大的后盾——抖音&#xff0c;以及推出的"中视频计划"。这个计划慷慨地斥资20亿用于支持视频制作者&#xff0c;因此在西瓜视频平台上&#xff0c;我们目睹了大量优质的长视频如雨后春笋般涌现。 对于…

SpringCloud 各自组件的停更/升级/替换

一、停更不停用 现在 SpringCloud 不再修复 bug&#xff0c;也不再接收合并请求&#xff0c;也不再发布新版本&#xff0c;但是目前还是可以继续使用的。 二、以前的组件 以前 SpringCloud 常用的组件如下图&#xff0c;服务的注册和发现使用 Eureka&#xff0c;服务的负载和调…

工业镜头的重要参数之视场、放大倍率、芯片尺寸--51camera

今天来简单介绍下工业镜头中常用的参数中的三个&#xff1a; 1、视场 视场&#xff08;FOV&#xff09;也称视野,是指能被视觉系统观察到的物方可视范围。 对于镜头而言&#xff0c;可观察到的视场跟镜头放大倍率及相机芯片选择有关。因此需要根据被观察物体的尺寸&#xff…

threejs展示glb模型

原模型为rvt模型 <template><div ref"threeJsContainer"class"three-js-container"></div> </template> <script> import { defineComponent } from "vue"; import * as THREE from "three"; import…

如何选择阿里云服务器配置,过来人说说

阿里云服务器配置怎么选择&#xff1f;CPU内存、公网带宽和系统盘怎么选择&#xff1f;个人开发者或中小企业选择轻量应用服务器、ECS经济型e实例&#xff0c;企业用户选择ECS通用算力型u1云服务器、ECS计算型c7、通用型g7云服务器&#xff0c;阿里云服务器网aliyunfuwuqi.com整…

C++核心编程之内存分区模型,引用,函数提高

1&#xff0c;类型分区模型 c程序在执行中&#xff0c;将内存大方向划分为4个区域 1&#xff0c;代码区&#xff1a;存放函数体的二进制代码&#xff0c;由操作系统进行管理的 2&#xff0c;全局区&#xff1a;存放全局变量和静态变量以及常量 3&#xff0c;栈区&#xff1…

【学习笔记】java项目:黑马头条(day01)

文章目录 环境搭建、SpringCloud微服务(注册发现、服务调用、网关)1)课程对比2)项目概述2.1)能让你收获什么2.2)项目课程大纲2.3)项目概述2.4)项目术语2.5)业务说明 3)技术栈4)nacos环境搭建4.1)虚拟机镜像准备4.2)nacos安装 5)初始工程搭建5.1)环境准备5.2)主体结构 6)登录6.1…

Openfeign+Ribbon+Hystrix断路器(服务降级)

热部署对于Hystrix的热不是不是很明显 所以最好修改代码之后重启服务 简介 在微服务架构中存在多个可直接调用的服务,这些服务若在调用时出现故障会导致连锁效应,也就是可能让整个系统变得不可用,这种情况我们称之为服务雪崩效应. 服务雪崩效应通常发生在微服务架构中&…

应用监控 eBPF 版:实现 Golang 微服务的无侵入应用监控

作者&#xff1a;古琦 在现代软件架构中&#xff0c;微服务已成为构建可扩展和灵活应用程序的流行方式。每个微服务负责应用程序的一部分功能&#xff0c;它们共同工作以提供完整的服务。由于微服务架构的分散特性&#xff0c;监控变得至关重要&#xff0c;有效的微服务监控是…

缺陷检测:使用PatchCore训练自己的数据集

文章目录 前期准备两种方法 演示运行结果 代码详解见缺陷检测–PatchCore的代码解读 前期准备 必须包含有训练图片&#xff08;无缺陷图片&#xff09;、测试图片&#xff08;缺陷图片&#xff09;和ground_truth&#xff0c;并且ground_truth必须与对应图片的名称相同。 本文…

python_读取txt文件绘制多条曲线II

从给定的列表中来匹配txt文件对应列的数据&#xff1b; import matplotlib.pyplot as plt import re from datetime import datetime from pylab import mplmpl.rcParams["font.sans-serif"] ["SimHei"] # 设置显示中文字体 mpl.rcParams["axes.un…

STM32用标准库做定时器定时1秒更新OLED的计数值(Proteus仿真)

首先新建proteus工程&#xff0c;绘制电路图&#xff1a; 然后赋值我之前文章中提到的文件夹OLED屏幕显示&#xff1a;&#xff08;没有的自己去那篇文章下载去&#xff09; 然后进入文件夹&#xff1a; 新建两个文件在Mycode文件夹中&#xff1a; 文件关系如下&#xff1a; 新…

lowcode-engine接入编辑器

https://lowcode-engine.cn/site/docs/guide/create/useEditor 方案1 pnpm init pnpm add "alilc/create-elementlatest"pnpm create "alilc/element" editor-project-name选择编辑器 进入执行pnpm install命令安装包 pnpm start报错 pnpm add &qu…

springboot-异步、定时、邮件任务

一、异步任务 1、创建项目 2、创建一个service包 3、创建一个类AsyncService 异步处理还是非常常用的&#xff0c;比如我们在网站上发送邮件&#xff0c;后台会去发送邮件&#xff0c;此时前台会造成响应不动&#xff0c;直到邮件发送完毕&#xff0c;响应才会成功&#xff…

高校的虚拟仿真实训室为何要创建数字人实训室?

随着教育信息化、数字化的不断发展&#xff0c;虚拟仿真实训室不再仅限于vr虚拟仿真实训室&#xff0c;数字人实训室也逐步应用于教育领域。 高校虚拟仿真实训室创建数字人实训室课堂&#xff0c;具有两大作用&#xff1a; 1.获得隐形实践知识 在虚拟仿真实训室环境下&#xf…

2024Java开发现状分析,字节java面试题

1. 前言 最近面试了几家公司&#xff0c;体验了一下电话面试和今年刚火起来的视频面试&#xff0c; 虽然之前就有一些公司会先通过电话面试的形式先评估下候选人的能力水平&#xff0c;但好像不多&#xff0c;至少我以前的面试形式100%都是现场面试。 面试过程中&#xff0c…

QT和OPENGL安装和集成

1.QT安装 1.1官网下载&#xff1a; 网址&#xff1a;https://download.qt.io/archive/qt/ 1.2 开始安装 点击运行 首先注册sign up 然后Login in 选择安装目录 改为D盘&#xff1a; 选择安装项&#xff1a; 准备安装 开始安装&#xff1a; 安装完成&#xff1a; 1.3测试 …