Spring框架中的@Conditional系列注解

news2024/11/28 10:37:13

目录

  • 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 {
 
    @Override
    public 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)
    @Bean
    public Student student(){
        return new Student();
    }
 
    @Conditional(LinuxCondition.class)
    @Bean
    public Teacher teacher(){
        return new Teacher();
    }
 
    @Conditional(WindowsCondition.class)
    @Bean
    public 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 {
 
    @Override
    public 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)
    @Bean
    public 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() {
            @Override
            protected 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, 那么该配置类就会跳过,这样配置就能使缓存不生效。

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

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

相关文章

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

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

18. 线性代数 - 线性变换

文章目录 线性空间线性变换线性变换的几何意义特征值与特征向量NumPy的矩阵操作Hi, 你好。我是茶桁。 经历了几节线性代数课程之后,终于咱们到了最后一节课了。本节课的内容说多不多,说少也不少。 我们先是要理解一下线性空间和线性变换,并且探讨一下线性变换的几何意义。…

Mapbox加载arcgis的底图

成果图 这种底图基本上都是按照raster来加载的&#xff0c;主要就是知道地址了&#xff0c;拼参数 具体参数请参考官网 https://developers.arcgis.com/rest/services-reference/enterprise/export-map.htm 源码 我的服务列表是这样的 http://XXXX:XXXX/arcgis/rest/services/…

Rsync远程同步inotify监控

Rsync 简介 rsync&#xff08;Remote Sync&#xff0c;远程同步&#xff09; 是一个开源的快速备份工具&#xff0c;可以在不同主机之间镜像同步整个目录树&#xff0c;支持增量备份&#xff0c;并保持链接和权限 在远程同步任务中&#xff0c;负责发起rsync同步操作的客户机…

【Docker】Docker简介

Docker简介 &#x1f4cb;导航 1. Docker简介1.1 什么是Docker&#xff1f;1.2 什么是容器&#xff1f;1.3 容器的优势&#xff1f;1.4 Docker的优势&#xff1f;1.5 虚拟技术与容器技术Docker的区别&#xff1f;1.6 为什么学习Docker? 2. 安装Docker3. Docker架构4. Docker命…

【算法训练-栈 一】【结构特性】有效的括号

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【栈的使用】&#xff0c;使用【栈】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为&#x…

影刀RPA解决WPS不存在的问题

问题阐述 明明电脑上已经安装了WPS&#xff0c;但影刀程序还是提示没有安装的问题 解决办法 1.打开WPS并关闭所有其他网页 2. 配置与修复 3.开始修复 出现这个框&#xff0c;就要关闭WPS&#xff0c;否则无法执行&#xff0c;关闭WPS不影响其修复 4.等待修复完成即可

《打造高可用PostgreSQL:策略与工具》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

性能测试、负载测试、压力测试、稳定性测试简单区分

是一个总称&#xff0c;可细分为性能测试、负载测试、压力测试、稳定性测试。 性能测试 以系统设计初期规划的性能指标为预期目标&#xff0c;对系统不断施加压力&#xff0c;验证系统在资源可接受范围内&#xff0c;是否能达到性能瓶颈。 关键词提取理解 有性能指标&#…

【SpringMVC】JSON数据传输与异常处理的使用

文章目录 一、Jackson1.1 Jackson是什么1.2 常用注解1.3 实例1.3.1导入依赖1.3.2 配置spring-mvc.xml1.3.3 JsonController.java 二、Spring MVC异常处理机制2.1 使用原因2.2 SpringMVC异常处理2.2.1 异常处理机制流程图2.2.2 异常处理的三种方式 一、Jackson 1.1 Jackson是什…

Spring Boot配置文件(YAML Properties)总结

文章目录 配置文件的作用YAML配置文件Properties配置文件配置文件的加载顺序激活不同的配置文件配置文件的占位符自定义配置属性加密敏感信息配置文件的最佳实践结论 &#x1f389;欢迎来到架构设计专栏~Spring Boot配置文件&#xff08;YAML & Properties&#xff09;总结…

解决Java应用程序中的SQLException:Access denied for user ‘root‘@‘localhost‘ 错误

目录 问题背景 解决方案 如何重置 MySQL root 密码&#xff1a; 问题背景 java.sql.SQLException: Access denied for user rootlocalhost (using password: YES) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:127) at com.mysql.cj.jdbc.ex…

树控件、下拉框、文本框常用测试用例

01 控件的测试外观操作 1&#xff09;项目中的所有树是否风格一致 2&#xff09;树结构的默认状态是怎样的。比如默认树是否是展开&#xff0c;是展开几级&#xff1f; 是否有默认的焦点&#xff1f;默认值是什么&#xff1f;展开的节点图标和颜色&#xff1f; 3&#xff09…

springboot整合actuator、admin对应用程序进行监控

Spring Boot Actuator 是 Spring Boot 的一个子项目&#xff0c;可以对 Spring Boot 应用程序进行监控和管理&#xff0c;并对外提供了大量的端点&#xff0c;可以选择使用 HTTP 端点或 JMX 来管理和监控应用程序。 这篇文章主要介绍我们的应用程序中怎么加入actuator来对应用进…

算法课程入门

1、算法这门课&#xff0c;主要讲这三件事&#xff1a; 状态空间 最优可行解问题。 确定与非确定。 状态空间的思维方式要掌握住&#xff0c;要能使用状态空间解决新问题。 2、课程安排&#xff1a; 前几本也要看&#xff0c;但是最后一本强烈推荐。 3、经验之谈&#xff1a; …

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

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

TypeScript泛型和类型体操

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 泛型&#xff08;Generics&#xff09; 1. 泛型函数 2. 泛型接口 3. 泛型类 类型体操&#xff08;Type Gymnast…

leetcode 232 用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int peek() 返回队列开头…

Python实现天文计算

迷途小书童 读完需要 2分钟 速读仅需 1 分钟 1 简介 ephem 模块为 Python 提供了精确的天文计算能力&#xff0c;可以预测星球、卫星的轨道信息&#xff0c;计算日出日落、经星时间等数据&#xff0c;它的算法准确可靠。最初由 Brandon Craig Rhodes 在 20 世纪 90 年代开发&am…

数字IC验证23912--寄存器模型

文章目录 寄存器模型的集成总线UVC的实现总线UVC的示例Adapter的实现Adapter的集成 访问方式前门访问后门访问 寄存器模型的集成 总线UVC的实现 MCDF访问寄存器的总线接口时序较为简单。控制寄存器接口首先需要在每一个时钟解析cmd。当cmd为写指令时&#xff0c;即需要把数据c…