Spring Boot2.x条件注解@Conditional详解及实战

简介: Spring Boot2.x条件注解@Conditional详解及实战

image.png@Conditional是Spring Boot中大量使用的注解之一,它可以根据是否满足某一个特定条件来决定是否加载指定的Bean。本文带领大家详细了解该注解的基本功能及实战使用。

条件注解@Conditional

@Conditional是SpringFramework提供的注解,位于 org.springframework.context.annotation包内,被其注解的类会根据指定的条件进行判断,如果满足条件则进行Bean的实例化及加载,如果不符合条件则不进行加载。

比如在Spring Boot的自动配置中经常用在这样的场景:当某个待自动配置组件的jar包在类路径下时,自动配置该组件的一个或多个Bean。

除了直接使用@Conditional注解来进行判断,在Spring Boot中通常情况下使用的更多的是由@Conditional组合的具体特殊场景的注解。比如,@ConditionalOnClass注解用来检查类路径下是否有指定的类。其相关使用源码如下:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
// ...
}

@Conditional使用形式:

  • 类型级别,可以在@Component或是@Configuration类上使用;
  • 原型级别,用于特定自动场景注解上的注解(如上:ConditionalOnClass);
  • 方法级别,作用在任何@Bean方法上(如下例)。

另外,condition注解是不会继承的,如果一个父类使用了conditional注解,其子类是不会拥有conditions的。

源码解析

为了更好的理解@Conditional注解的使用,我们先看一下它的源代码定义:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    // 该属性传入实现Conditions的类
    Class<? extends Condition>[] value();
}

通过源码可以看出,Conditional的属性为实现了Condition接口的类的数组。在使用的过程中只有满足了Condition接口实现类定义的条件才会进行实例化操作。

顺便再看一下Condition接口的定义:





@FunctionalInterfacepublic interface Condition {  boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);}

其中只定义了一个返回类型为boolean的方法,也就是说返回值为true则被注解类进行实例化,返回值为false则不进行实例化。

实例讲解

下面我们先通过一个实例来演示一下@Conditional注解的具体使用。了解了@Conditional的基本功能和使用场景,要实现具体的例子,大概可分为两步:第一,实现Condition接口定义实例化场景;第二,在具体的业务场景中使用@Conditional注解并指定其属性为上面的实现类。

首先定义一个简单的模型来实现:配置文件中可以指定数据库的类型,当指定不同的类型时,实例化不同的Bean。

配置文件中配置属性如下:

db.type=mysql

针对该配置文件定义Mysql和Oracle的Condition条件判断实现。

Mysql的Condition实现:








public class MysqlCondition implements Condition {  @Override  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {    String dbType = context.getEnvironment().getProperty("db.type");    return "mysql".equalsIgnoreCase(dbType);  }}

Oralce的Condition实现:








public class OracleCondition implements Condition {  @Override  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {    String dbType = context.getEnvironment().getProperty("db.type");    return "oracle".equalsIgnoreCase(dbType);  }}

两个Condition实现很简单,从环境变量中获得指定配置属性值,如果匹配对应的字符串,则表示符合条件并返回。

下面针对上面的定义进行使用,为了方便调用我们让对应的Mysql和Oracle配置实现均实现同一个接口ConfigBean。




public interface DbConfigBean {  void printInfo();}

具体实现如下:







public class MysqlDbConfigBean implements DbConfigBean {  @Override  public void printInfo(){    System.out.println("I am Mysql!");  }}







public class OracleDbConfigBean implements DbConfigBean {
  @Override  public void printInfo(){    System.out.println("I am Oracle!");  }}

也就是说这两个实现类,都有打印对应Bean的信息的方法。

此时通过@Conditional来对这两个Bean的实例化进行注解判断。
















@Configurationpublic class DbConfig {
  @Bean  @Conditional(MysqlCondition.class)  public DbConfigBean mysql() {    return new MysqlDbConfigBean();  }
  @Bean  @Conditional(OracleCondition.class)  public DbConfigBean oracle() {    return new OracleDbConfigBean();  }}

在配置类DbConfig中,定义了实例化DbConfigBean的两个方法,每个方法都通过@Conditional指定实例化的条件,当满足条件时就会创建对应的Bean。

注意此处两个Bean的实例化是互斥的,如果在实践过程中两个Bean有可能同时存在则使用@Primary注解来定义同时存在时使用哪个。

至此,@Conditional的定义和使用便完成了,下面看对具体Bean的使用和单元测试。

具体使用示例:











@Servicepublic class DbService {
  @Autowired  private DbConfigBean configBean;    public void getDbInfo(){    configBean.printInfo();  }}

单元测试示例:












@SpringBootTestclass DbServiceTest {
  @Resource  private DbService dbService;    @Test  void getDbInfo() {    dbService.getDbInfo();  }}

此时根据配置文件中配置的是mysql还是oracle会调用对应的方法进行打印。

我们这里是针对指定的Bean使用@Conditional,如果一个@Configuration类被标注为@Conditional,则该类的所有的@Bean方法, @Import注解和@ComponentScan注解,都依从于该条件。

@Conditional衍生注解

@Conditional由Spring提供,而在Spring Boot中衍生出了以下相关的注解:

  • @ConditionalOnBean:当容器中有指定Bean的条件下。
  • @ConditionalOnClass:当classpath类路径下有指定类的条件下。
  • @ConditionalOnCloudPlatform:当指定的云平台处于active状态时。
  • @ConditionalOnExpression:基于SpEL表达式的条件判断。
  • @ConditionalOnJava:基于JVM版本作为判断条件。
  • @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置。
  • @ConditionalOnMissingBean:当容器里没有指定Bean的条件。
  • @ConditionalOnMissingClass:当类路径下没有指定类的条件下。
  • @ConditionalOnNotWebApplication:当项目不是一个Web项目的条件下。
  • @ConditionalOnProperty:当指定的属性有指定的值的条件下。
  • @ConditionalOnResource:类路径是否有指定的值。
  • @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean。
  • @ConditionalOnWebApplication:当项目是一个Web项目的条件下。

以上组合注解均位于spring-boot-autoconfigure jar包下的org.springframework.boot.autoconfigure.condition包下。


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
10月前
|
缓存 监控 Java
SpringBoot @Scheduled 注解详解
使用`@Scheduled`注解实现方法周期性执行,支持固定间隔、延迟或Cron表达式触发,基于Spring Task,适用于日志清理、数据同步等定时任务场景。需启用`@EnableScheduling`,注意线程阻塞与分布式重复问题,推荐结合`@Async`异步处理,提升任务调度效率。
1493 128
|
9月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
1379 2
Spring Boot 3.x 微服务架构实战指南
|
10月前
|
消息中间件 Ubuntu Java
SpringBoot整合MQTT实战:基于EMQX实现双向设备通信
本教程指导在Ubuntu上部署EMQX 5.9.0并集成Spring Boot实现MQTT双向通信,涵盖服务器搭建、客户端配置及生产实践,助您快速构建企业级物联网消息系统。
3017 1
|
9月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
9月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
774 2
|
10月前
|
XML Java 数据格式
常用SpringBoot注解汇总与用法说明
这些注解的使用和组合是Spring Boot快速开发和微服务实现的基础,通过它们,可以有效地指导Spring容器进行类发现、自动装配、配置、代理和管理等核心功能。开发者应当根据项目实际需求,运用这些注解来优化代码结构和服务逻辑。
645 12
|
JSON 前端开发 Java
SpringBoot 实战:一招实现结果的优雅响应
今天说一下 Spring Boot 如何实现优雅的数据响应:统一的结果响应格式、简单的数据封装。
698 0
SpringBoot 实战:一招实现结果的优雅响应
|
9月前
|
JavaScript Java 关系型数据库
基于springboot的项目管理系统
本文探讨项目管理系统在现代企业中的应用与实现,分析其研究背景、意义及现状,阐述基于SSM、Java、MySQL和Vue等技术构建系统的关键方法,展现其在提升管理效率、协同水平与风险管控方面的价值。