在Spring
的应用下,我们希望一些bean
可以通过一些条件来判断是否需要实例化,并加载到spring
容器中。
所以,@Conditional
注解就是为了解决上面这个需求而制定的注解。@Conditional
注解是总接口,可以定制逻辑。
先看源码,此注解需要传入Condition
接口的实现类,可以多个
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
所以,使用此注解时,我们若是有些高度定制化的一些判断,可以先实现Condition
接口,再讲实现类提供给@Conditional
注解,使用示例如下。
首先要有一个Condition
接口的实现类,可以看看框架中其他的实现类,这边自己实现一个
package com.banmoon.test;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class TestCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO 定制化逻辑,比如说数据库的某项配置
return true;
}
}
再进行其进行判断并初始化bean
@SpringBootApplication
public class TestApplication {
@Bean("conditional")
@Conditional(TestCondition.class)
public String conditional() {
return "conditional";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
String string = run.getBean("conditional", String.class);
System.out.println(string);
System.out.println(System.lineSeparator());
}
}
运行结果如下,成功获取到bean
@ConditionalOnBean
和@ConditionalOnMissingBean
是相反对应的一组注解,看注解名称也可以看出来。
前者是判断存在某个bean
,后者是判断是否确实某个bean
。
先来看看他们的使用,如下
@SpringBootApplication
public class TestApplication {
@Bean("conditionalOnBean")
@ConditionalOnBean(TestApplication.class)
public String conditionalOnBean() {
return "conditionalOnBean";
}
@Bean("conditionalOnMissingBean")
@ConditionalOnMissingBean(TestApplication.class)
public String conditionalOnMissingBean() {
return "conditionalOnMissingBean";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
Map<String, String> beanMap = run.getBeansOfType(String.class);
beanMap.forEach((k, v) -> System.out.println(StrUtil.format("{}:{}", k, v)));
System.out.println(System.lineSeparator());
}
}
可以看到只有一个bean
被创建出来,另外一个由于不满足条件,所以没有创建bean
。
查看注解源码,发现除了可以传Class
对象,还可以传其他的属性来进行确定
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
/**
* 检查bean的class类型,当指定的所有类的 bean 都包含在 BeanFactory 中时,此条件才匹配
*/
Class<?>[] value() default {};
/**
* 检查bean的权限名,当指定的所有类的 bean 都包含在 BeanFactory 中时,此条件才匹配
*/
String[] type() default {};
/**
* 检查bean的注解类型,当指定的所有注解都在 BeanFactory 中的 bean 上定义时,此条件才匹配
*/
Class<? extends Annotation>[] annotation() default {};
/**
* 要检查的 bean 的名称。当指定的所有 bean 名称都包含在 BeanFactory 中时,此条件才匹配。
*/
String[] name() default {};
/**
* 考虑应用程序的上下文结构的策略
*/
SearchStrategy search() default SearchStrategy.ALL;
/**
* 可能在其通用参数中包含指定 bean 类型的其他类。例如,声明 value=Name.class 和 parameterizedContainer=NameRegistration.class 的注释将同时检测 Name 和 NameRegistration<Name>。
*/
Class<?>[] parameterizedContainer() default {};
}
看名字也就能看的出来,是指从配置文件加载,用来进行条件判断。使用如下
package com.banmoon.test;
import cn.hutool.core.util.StrUtil;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Map;
@SpringBootApplication
public class TestApplication {
@Bean("conditionalOnProperty")
@ConditionalOnProperty(value = "conditionalOnProperty", prefix = "banmoon-condition")
public String conditionalOnProperty() {
return "conditionalOnProperty";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
Map<String, String> beanMap = run.getBeansOfType(String.class);
beanMap.forEach((k, v) -> System.out.println(StrUtil.format("{}:{}", k, v)));
System.out.println(System.lineSeparator());
}
}
还是点入进去看看该注解的源码
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
/**
* 同name属性一致
*/
String[] value() default {};
/**
* 应用于每个属性的前缀。如果未指定,前缀会自动以点结尾。有效前缀由一个或多个用点分隔的单词定义(例如“acme.system.feature”)
*/
String prefix() default "";
/**
* 要测试的属性的名称。如果已定义前缀,则将其应用于计算每个属性的完整键。例如,如果前缀是 app.config 并且一个值是 my-value,则完整的键将是 app.config.my-value 使用虚线表示法来指定每个属性,即全部小写,用“-”分隔单词(例如 my-long-property)
*/
String[] name() default {};
/**
* 属性预期值的字符串表示形式。如果未指定,则该属性不得等于 false
*/
String havingValue() default "";
/**
* 如果未设置属性,则指定条件是否应匹配。默认为false
*/
boolean matchIfMissing() default false;
}
还有一段不好翻译,给予截图吧,指的是havingValue
属性的使用。
这些比较特殊,不同的属性值和不同的havingValue
组合,可以得到什么样的结果。
@ConditionalOnClass
和@ConditionalOnMissingClass
也是一组相对的注解,他们的功能是判断是否拥有java
类
@SpringBootApplication
public class TestApplication {
@Bean("conditionalOnClass")
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public String conditionalOnClass() {
return "conditionalOnClass";
}
@Bean("conditionalOnMissingClass")
@ConditionalOnMissingClass(value = "oracle.jdbc.driver.OracleDriver")
public String conditionalOnMissingClass() {
return "conditionalOnMissingClass";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
Map<String, String> beanMap = run.getBeansOfType(String.class);
beanMap.forEach((k, v) -> System.out.println(StrUtil.format("{}:{}", k, v)));
System.out.println(System.lineSeparator());
}
}
由于我只添加了MySQL
的驱动,没有加上Oracle
,所以结果如下
@ConditionalOnWebApplication
与@ConditionalOnNotWebApplication
是一组相对的注解,指的判断是否是web
环境应用。
package com.banmoon.test;
import cn.hutool.core.util.StrUtil;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import java.util.Map;
@SpringBootApplication
public class TestApplication {
@Bean("conditionalOnWebApplication")
@ConditionalOnWebApplication
public String conditionalOnWebApplication() {
return "conditionalOnWebApplication";
}
@Bean("conditionalOnNotWebApplication")
@ConditionalOnNotWebApplication
public String conditionalOnNotWebApplication() {
return "conditionalOnNotWebApplication";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
Map<String, String> beanMap = run.getBeansOfType(String.class);
beanMap.forEach((k, v) -> System.out.println(StrUtil.format("{}:{}", k, v)));
System.out.println(System.lineSeparator());
}
}
@ConditionalOnJava
用来判断当前java
的版本
@SpringBootApplication
public class TestApplication {
@Bean("conditionalOnJava")
@ConditionalOnJava(value = JavaVersion.EIGHT)
public String conditionalOnJava() {
return "conditionalOnJava";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
Map<String, String> beanMap = run.getBeansOfType(String.class);
beanMap.forEach((k, v) -> System.out.println(StrUtil.format("{}:{}", k, v)));
System.out.println(System.lineSeparator());
}
}
看下注解的源码,比较简单
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnJavaCondition.class)
public @interface ConditionalOnJava {
/**
* 配合value()使用,来确定范围。默认是Range.EQUAL_OR_NEWER,也就是比指定范围高
*/
Range range() default Range.EQUAL_OR_NEWER;
/**
* 指定Java版本
*/
JavaVersion value();
enum Range {
/**
* 大于等于
*/
EQUAL_OR_NEWER,
/**
* 低于
*/
OLDER_THAN
}
}
使用Spring
表达式来进行判断,也就是SpEL
表达式
@SpringBootApplication
public class TestApplication {
@Bean("intValue")
public Integer intValue() {
return 10;
}
@Bean("conditionalOnExpression")
@ConditionalOnExpression("#{intValue>5}")
public String conditionalOnExpression() {
return "conditionalOnExpression";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
Map<String, String> beanMap = run.getBeansOfType(String.class);
beanMap.forEach((k, v) -> System.out.println(StrUtil.format("{}:{}", k, v)));
System.out.println(System.lineSeparator());
}
}
@ConditionalOnResource
是否有指定的静态资源,示例如下
@SpringBootApplication
public class TestApplication {
@Bean("conditionalOnResource")
@ConditionalOnResource(resources = "application.yml")
public String conditionalOnResource() {
return "conditionalOnResource";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
Map<String, String> beanMap = run.getBeansOfType(String.class);
beanMap.forEach((k, v) -> System.out.println(StrUtil.format("{}:{}", k, v)));
System.out.println(System.lineSeparator());
}
}
@ConditionalOnSingleCandidate
,判断指定的类型是否只有一个bean
,示例如下
@SpringBootApplication
public class TestApplication {
@Bean("intValue")
public Integer intValue(){
return 10;
}
@Bean("intValue2")
public Integer intValue2(){
return 20;
}
@Bean("doubleValue")
public Double doubleValue(){
return 10D;
}
@Bean("intValueConditional")
@ConditionalOnSingleCandidate(Integer.class)
public String intValueConditional() {
return "intValueConditional";
}
@Bean("doubleValueConditional")
@ConditionalOnSingleCandidate(Double.class)
public String doubleValueConditional() {
return "doubleValueConditional";
}
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(TestApplication.class, args);
Map<String, String> beanMap = run.getBeansOfType(String.class);
beanMap.forEach((k, v) -> System.out.println(StrUtil.format("{}:{}", k, v)));
System.out.println(System.lineSeparator());
}
}
整理一下注解表格,其中有一些上面没有列出来的注解
注解 | 说明 |
---|---|
@Conditional | 传入一个Condition接口的实现类,来进行判断 |
@ConditionalOnBean | 判断是否存在某个bean |
@ConditionalOnMissingBean | 判断是否缺失了某个bean |
@ConditionalOnProperty | 判断配置文件中的某项配置 |
@ConditionalOnClass | 判断是否拥有某个java类 |
@ConditionalOnMissingClass | 判断是否缺失了某个java类 |
@ConditionalOnWebApplication | 判断当前是否为web环境 |
@ConditionalOnNotWebApplication | 判断当前是否不为web环境 |
@ConditionalOnJava | 判断当前java运行版本 |
@ConditionalOnExpression | 使用Spring表达式来进行判断,也就是SpEL表达式 |
@ConditionalOnResource | 判断是否有指定的静态资源 |
@ConditionalOnSingleCandidate | 判断指定的类型是否只有一个bean |
@ConditionalOnJndi | 通过JNDI进行判断 |
@ConditionalOnCloudPlatform | 判断当前环境是否是云平台 |
@ConditionalOnWarDeployment | 判断当前是否是War包环境 |
@ConditionalOnMissingFilterBean | 判断是否缺失了某个bean过滤器 |
我是半月,祝你幸福!!!