首页 > 其他分享 >条件注解@Conditional

条件注解@Conditional

时间:2023-08-18 18:03:49浏览次数:28  
标签:String default Conditional class Bean 条件 注解 public

定义:在spring框架中用于根据特定条件决定是否创建或者注册某个bean或配置的注解,他们可以根据运行时环境,配置属性,或其他条件来动态的控制bean的创建或者注册。

@Conditional注解

定义:基本上所有的条件注解,都是基于该注解进行的扩展。此注解从Spring4.0之后开始使用,一般用来限制配置类是否生效或者某个bean是否需要注入。

@Conditional源码如下:

@Target({ElementType.TYPE, ElementType.METHOD})//标注此注解可以使用在类和方法上
@Retention(RetentionPolicy.RUNTIME)//此注解在运行时保留
@Documented//注解包含在javadoc中生成的文档中
public @interface Conditional {
    Class<? extends Condition>[] value();
}

@interface 是定义注解的关键字。

上述注解定义了一个value的属性,value是一个数组类型,用于存储class对象,这里class对象类型,被限定为Condition类或其子类

看一下conditional接口

@FunctionalInterface//函数接口:只包含一个抽象方法的接口
public interface Condition {
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
//context:条件上下文、提供了访问环境、Bean定义和其他条件信息的方法。
//metadata:注解元数据,提供了访问条件注解上的属性和其他相关注解信息的方法。

在 Spring 中,matches 方法的返回值表示条件是否满足。条件的判断依据可以根据具体的需求和上下文来确定。

通常情况下,matches 方法的实现可以使用条件上下文(ConditionContext)和注解元数据(AnnotatedTypeMetadata)提供的信息进行条件判断。以下是一些常见的判断依据:

  1. 条件上下文信息:
  • 环境信息:可以获取当前运行环境的属性、配置、名称等。
  • Bean 定义信息:可以获取正在加载的 Bean 定义的属性、注解等。
  • 资源加载器:可以访问类路径资源和文件系统资源等。
  • 类加载器:可以加载和检查类、接口和注解等。
  • Bean 工厂:可以获取已注册的 Bean 实例、检查它们的类型等。
  • 其他自定义信息:可以通过添加自定义属性或其他自定义方式提供额外的信息。
  1. 注解元数据信息:
  • 条件注解属性:可以通过 metadata 对象获取条件注解上定义的属性值,以决定是否满足条件。
  • 其他相关注解:可以通过 metadata 对象获取与条件注解关联的其他注解信息,以进一步判断条件。

根据具体的业务需求和上下文信息,可以使用这些信息来编写判断逻辑。常见的判断依据包括但不限于:

  • 环境变量的值
  • 配置属性的值
  • Bean 定义的属性或注解
  • 类路径资源或文件系统资源的存在
  • 特定的类、接口或注解是否存在
  • 条件注解的属性值是否满足特定要求
  • 其他自定义条件判断逻辑

需要根据具体的应用场景和条件注解的需求来确定具体的判断依据。

使用:

@Conditional注解一般使用在@Configuration配置类上,用于限制该配置类是否。

该注解也可以定义在方法上和@Bean注解配合使用,用于判断该bean是否需要注入。

示例:

/**
 * @author junkai.pan
 */
//注意这里是condition不是conditional  因为@Conditional需要的属性为Condition类或其子类
public class WindowsCondition implements Condition {
    @Override
    public boolean matches(@NonNull ConditionContext context, @NonNull AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();//获取环境信息
        String systemName = environment.getProperty("os.name");//获取os(operating system 操作系统的简写)的name

        if (systemName != null) {
            return systemName.contains("Windows");//contains返回的结果是boolean  所以可以直接返回
        }//请注意这里的windows的w是大写的W  我因为这里弄了半天 emmm
        return false;
    }
}

上述方法:

1.创建了一个windowsCondition类;

2.实现了condition接口;

3.然后通过context上下文获取运行环境信息;

4.然后获取运行环境中的系统名;

5.通过判断系统名中是不是包含windows来决定条件是否成立。

运行结果:

2023-08-18 14:20:19.064  INFO 22060 --- [           main] c.s.demo.demo02.WindowsCondition         : systemName为:Windows 10
2023-08-18 14:20:19.064  INFO 22060 --- [           main] c.s.demo.demo02.WindowsCondition         : 条件判断成功。
2023-08-18 14:20:19.067  INFO 22060 --- [           main] c.s.demo.demo02.WindowsCondition         : systemName为:Windows 10
2023-08-18 14:20:19.067  INFO 22060 --- [           main] c.s.demo.demo02.WindowsCondition         : 条件判断成功。
2023-08-18 14:20:19.327  INFO 22060 --- [           main] c.s.demo.demo02.WindowsCondition         : systemName为:Windows 10
2023-08-18 14:20:19.327  INFO 22060 --- [           main] c.s.demo.demo02.WindowsCondition         : 条件判断成功。
2023-08-18 14:20:20.069  INFO 22060 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-08-18 14:20:20.082  INFO 22060 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-08-18 14:20:20.082  INFO 22060 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.65]
2023-08-18 14:20:20.192  INFO 22060 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-08-18 14:20:20.192  INFO 22060 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2174 ms
2023-08-18 14:20:20.247  INFO 22060 --- [           main] c.springBatch.demo.demo02.ConditionTest  : condition----ok

扩展:

Springboot中扩展的条件注解一共有14个,都在org.springframework.boot.autoconfigure.condition包下

  1. @ConditionalOnBean
  2. @ConditionalOnClass
  3. @ConditionalOnCloudPlatform
  4. @ConditionalOnExpression
  5. @ConditionalOnJava
  6. @ConditionalOnJndi
  7. @ConditionalOnMissingBean
  8. @ConditionalOnMissingClass
  9. @ConditionalOnNotWebApplication
  10. @ConditionalOnProperty
  11. @ConditionalOnResource
  12. @ConditionalOnSingleCandidate
  13. @ConditionalOnWarDeployment
  14. @ConditionalOnWebApplication

通过查看上述各个注解的源码,我们可以了解到,它们都可以作用在类和方法上。

比如:@Configuration、@Component、@Service、@Controller、@Repository、@Mapper等都可以通过添加响应的@ConditionalOnXXXX,来判断是否可以加载。

下面我们对比较常见的条件注解做解释

@ConditionalOnProperty

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
    String[] value() default {};

    String prefix() default "";

    String[] name() default {};

    String havingValue() default "";

    boolean matchIfMissing() default false;
}

作用:

当配置文件,application.propertites/application.yml文件中存在指定属性,该条件为true;

举例:

作用在类上:

@ConditionalOnProperty(prefix = "spring.datasource",name = "type")
@Configuration
@Slf4j
public class TestConditionalOnProperty {
    @Bean
    public String contionalOnProperty() {
        log.info("注入-------contionalOnProperty------ok");
        return "ok";
    }
}

在上述代码中,表示会去application文件中,读取前缀为:Spring.datasource下,名字为type的key的值,读取到之后,会跟字符串false进行判断,如果获取的值等于 false 那么就返回false,不加载,如果不等于flase,那么就加载

我们的配置文件application.yml中的配置为:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource

因为 com.zaxxer.hikari.HikariDataSour != false 所以条件注解判断为true 进行注入。

运行结果:

2023-08-18 15:14:58.422  INFO 3956 --- [           main] c.springBatch.demo.demo02.ConditionTest  : condition----ok
2023-08-18 15:14:58.424  INFO 3956 --- [           main] c.s.d.demo02.TestConditionalOnProperty   : 注入-------contionalOnProperty------ok

作用在方法上:

@ConditionalOnProperty(prefix = "spring.datasource",name = "type")
@Configuration
@Slf4j
public class TestConditionalOnProperty {
    @Bean
    @ConditionalOnProperty(prefix = "spring.datasource",value = "username")
    public String contionalOnProperty() {
        log.info("注入-------contionalOnProperty------ok");
        return "ok";
    }
}

配置文件:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    username: false

运行结果:

上述因为我们读取的值为false,false==false 所以 bean注入失败。

下面我们修改下配置文件,

新配置文件:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    username: root

再次运行:

2023-08-18 15:18:49.103  INFO 18924 --- [           main] c.s.d.demo02.TestConditionalOnProperty   : 注入-------contionalOnProperty------ok

使用注意:

  • name和value必须存在一个,且不能同时存在。
  • 如果havingValue有值,则跟havingValue的值进行比较,相同为true,不同为flase。
    使用havingValue条件判断:
@ConditionalOnProperty(prefix = "spring.datasource",name = "type")
@Configuration
@Slf4j
public class TestConditionalOnProperty {
    @Bean
    @ConditionalOnProperty(name = "spring.datasource.username",havingValue = "root")
    public String contionalOnProperty() {
        log.info("注入-------contionalOnProperty------ok");
        return "ok";
    }
}

    }
}
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    username: root

结果同样是成功。

注意这里使用的是name,也可以使用value,但是这两个不管在什么条件下,必须要有一个才可以。

  • 如果没有havingValue,那么会采用prefix+name 或 prefix+value 的值,去和false比较,如果值等于false 那么失败, 如果不等于false 成功。
  • 如果prefix+name或prefix+value 没有匹配到值得话 通过添加 matchMissing 得值来进行判断 如果 matchMissing=true 那么注入 如果matchMissing=flase 那么失败。
@ConditionalOnProperty(prefix = "spring.datasource",name = "type")
@Configuration
@Slf4j
public class TestConditionalOnProperty {
    @Bean
    @ConditionalOnProperty(name = "spring.datasource.password",matchIfMissing = true)
    public String contionalOnProperty() {
        log.info("注入-------contionalOnProperty------ok");
        return "ok";
    }
}
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    username: root

上述运行结果也是true.

@ConditionalOnBean和@ConditionalOnMissingBean

源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnBean {
    Class<?>[] value() default {};

    String[] type() default {};

    Class<? extends Annotation>[] annotation() default {};

    String[] name() default {};

    SearchStrategy search() default SearchStrategy.ALL;

    Class<?>[] parameterizedContainer() default {};
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnMissingBean {
    Class<?>[] value() default {};

    String[] type() default {};

    Class<?>[] ignored() default {};

    String[] ignoredType() default {};

    Class<? extends Annotation>[] annotation() default {};

    String[] name() default {};

    SearchStrategy search() default SearchStrategy.ALL;

    Class<?>[] parameterizedContainer() default {};
}

作用:

当上下文中,存在指定bean得时候,才注入。

当上下文中,不存在指定bean得时候,才注入。

参数介绍:

  • Class<?>[] value() default {}:指定要检查是否存在的 Bean 的类型。可以是单个 Class 对象,也可以是 Class 对象数组。只有当至少存在一个指定类型的 Bean 时,条件才会匹配。
  • String[] type() default {}:类得全限定名,用于指定要检查是否存在的 Bean 类型。
  • Class<? extends Annotation>[] annotation() default {}:指定要检查是否存在的 Bean 上是否标注了指定的注解。可以是单个注解类型,也可以是注解类型数组。只有当至少存在一个带有指定注解的 Bean 时,条件才会匹配。
  • String[] name() default {}:指定要检查是否存在的 Bean 的名称。可以是单个字符串值,也可以是字符串数组。只有当至少存在一个指定名称的 Bean 时,条件才会匹配。
  • SearchStrategy search() default SearchStrategy.ALL:指定搜索 Bean 的策略。可选值为 SearchStrategy.ALLSearchStrategy.ANCESTORSSearchStrategy.CURRENT。默认值是 SearchStrategy.ALL,表示在当前上下文和所有祖先上下文中搜索 Bean。
  • Class<?>[] parameterizedContainer() default {}:用于检查是否存在以指定类作为泛型容器的 Bean。可以是单个 Class 对象,也可以是 Class 对象数组。只有当至少存在一个以指定类作为泛型容器的 Bean 时,条件才会匹配。

代码示例:

//这里仅仅是一个普通类,没有控制反转

@Data
public class TestClass {
    private String test;
}
@Configuration
@Slf4j
@ConditionalOnBean(TestClass.class)
public class TestConditionalOnBean {
    @Bean
    public String testConditionalOnBeanInstance() {
        log.info("--------testConditionalOnBean注入成功---------");
        return "testConditionalOnBean注入成功";
    }

}

我们得spring容器中没有testClass Bean 所以上述bean注入失败、

相反当我们采用OnMissing就可以了

@Configuration
@Slf4j
@ConditionalOnMissingBean(TestClass.class)
public class TestConditionalOnBean {
    @Bean
    public String testConditionalOnBeanInstance() {
        log.info("--------testConditionalOnBean注入成功---------");
        return "testConditionalOnBean注入成功";
    }

}
2023-08-18 16:31:13.460  INFO 21680 --- [           main] c.s.demo.demo02.TestConditionalOnBean    : --------testConditionalOnBean注入成功---------   }
}

下面我们再创建一个bean

@Configuration
@Slf4j
public class TestBean {
    @Bean
    public String testBeanInstance() {
        log.info("-----------testBean注入成功--------");
        return "testBean注入成功";
    }
}

这时候我们再使用On就成功了

@Configuration
@Slf4j
@ConditionalOnBean(TestBean.class)
public class TestConditionalOnBean {
    @Bean
    public String testConditionalOnBeanInstance() {
        log.info("--------testConditionalOnBean注入成功---------");
        return "testConditionalOnBean注入成功";
    }

}
2023-08-18 16:36:19.513  INFO 11472 --- [           main] c.s.demo.demo02.TestConditionalOnBean    : --------testConditionalOnBean注入成功---------

下面我们演示其他参数得使用

name演示:

@Configuration
@Slf4j
@ConditionalOnBean(name = "testBean")
public class TestConditionalOnBean {
    @Bean
    public String testConditionalOnBeanInstance() {
        log.info("--------testConditionalOnBean注入成功---------");
        return "testConditionalOnBean注入成功";
    }

}

value演示:

@Configuration
@Slf4j
@ConditionalOnBean(value = TestBean.class)
public class TestConditionalOnBean {
    @Bean
    public String testConditionalOnBeanInstance() {
        log.info("--------testConditionalOnBean注入成功---------");
        return "testConditionalOnBean注入成功";
    }

}

type演示: 这里注意type是包得全限定名

@Configuration
@Slf4j
@ConditionalOnBean(type = "com.springBatch.demo.demo02.TestBean")
public class TestConditionalOnBean {
    @Bean
    public String testConditionalOnBeanInstance() {
        log.info("--------testConditionalOnBean注入成功---------");
        return "testConditionalOnBean注入成功";
    }

}

annotation演示:

1.自定义注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}

2.在某一个bean中使用注解

@Configuration
@Slf4j
@TestAnnotation
public class TestBean {
    @Bean
    public String testBeanInstance() {
        log.info("-----------testBean注入成功--------");
        return "testBean注入成功";
    }
}

3.测试

@Configuration
@Slf4j
@ConditionalOnBean(annotation = TestAnnotation.class)
public class TestConditionalOnBean {
    @Bean
    public String testConditionalOnBeanInstance() {
        log.info("--------testConditionalOnBean注入成功---------");
        return "testConditionalOnBean注入成功";
    }

}

4.结果

2023-08-18 17:03:02.954  INFO 18460 --- [           main] c.s.demo.demo02.TestConditionalOnBean    : --------testConditionalOnBean注入成功---------
2023

标签:String,default,Conditional,class,Bean,条件,注解,public
From: https://blog.51cto.com/u_16205743/7139740

相关文章

  • 6 JavaScript条件判断
    6条件判断除了HTML以外.几乎所有的编程语言都有条件判断的功能.比如,python,我们用if语句来做条件判断.到了javascript中也是一样的,也使用javascript来做条件上的判断./*语法1*/if(条件1){代码块1}if(条件)a,b,c,d;该语法表示当条件为真.运......
  • 10、简化条件逻辑
    条件表达式的复杂度往往关系到整个软件的复杂度10.1、分解条件表达式避免条件表达式过长,难于理解,所以该封装封装10.2、合并条件表达式有时候,在我们的程序中包含这样一些条件检查,检查条件不同,但行为一致,在这种情况下,我们可以考虑合并条件检查,相反,如果你任务这些条件检......
  • vue2公共组件=》筛选条件
    源码<template><divclass="c__filter":style="`height:${showFilter?'auto':'47px'}`"v-if="filterNum>0"ref="tableFilter"><divclass="c_filte......
  • Django对不确定多条件进行求交集搜索
    使用Django的Q方法创建搜索条件:name=request.GET.get('name')pages=request.GET.get('pages')operator=request.GET.get('operator')date1=request.GET.get('date1')date2=re......
  • elementUI使用分页器以及搜索条件
    <template><div><!--搜索--><divstyle="float:left"><el-form:inline="true":model="formInline"class="demo-form-inline"size="mini"><el-form-......
  • @NotNull和@NonNull注解得区别
    今天敲代码得时候遇到了一个警告Notannotatedparameteroverrides@NonNullparameter 这个警告得原因是你这个类继承得那个类参数被@NonNull注解标记了,但是你实现类没有使用这个注解。我产生得地方是我的类继承ItemProcressor重写它得process方法得时候这时候,就产生疑问@No......
  • @transcational注解
    @transcational注解能用在任何springboot项目的组件的方法里也就是被@component修饰的类常见的service类的@service注解内部就有@component注解,所以可以直接使用@transcational注解进行回滚微服务就是@globaltranscational具体使用可能要配合异步线程或者一些异步消息时处......
  • (Java实体类比表字段多处理方案)注解忽略实体类属性
    背景实体类多添加了几个字段用于查询,如果项目中使用了mybatis或mybatisplus会导致找不到表中字段的错误Causedby:java.sql.SQLSyntaxErrorException:Unknowncolumn'create_start_time'in'fieldlist'解决项目中使用mybatisimportorg.springframework.data.annotat......
  • if语句条件判断大集合--------------------------------------python语言学习
    准备数据: ##实现成绩大于等于600为优秀,其他为普通等级上代码:importpandasaspddf=pd.read_excel('C:/Users/Administrator/Desktop/test1.xlsx',header=1)defscore_if(score):ifscore>=600:a="优秀"returnaelse:a="普通"......
  • 济南两化融合申报补助和条件是什么
    济南两化融合申报补助和条件是什么  恒标知产刘经理一、两化融合是指什么 定义: 两化融合是信息化和工业化的高层次的深度结合,是指以信息化带动工业化、以工业化促进信息化,走新型工业化道路;两化融合的核心就是信息化支撑,追求可持续发展模式。 两化融合是指电子信息技术广泛应......