SpringBoot中的@Conditional注解
一、介绍
在Spring
的应用下,我们希望一些bean
可以通过一些条件来判断是否需要实例化,并加载到spring
容器中。
所以,@Conditional
注解就是为了解决上面这个需求而制定的注解。@Conditional
注解是总接口,可以定制逻辑。
二、详情
1)@Conditional
先看源码,此注解需要传入Condition
接口的实现类,可以多个
1 2 3 4 5 6 7 8
| @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { Class<? extends Condition>[] value(); }
|
所以,使用此注解时,我们若是有些高度定制化的一些判断,可以先实现Condition
接口,再讲实现类提供给@Conditional
注解,使用示例如下。
首先要有一个Condition
接口的实现类,可以看看框架中其他的实现类,这边自己实现一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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) { return true; } }
|
再进行其进行判断并初始化bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @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
2)@ConditionalOnBean
@ConditionalOnBean
和@ConditionalOnMissingBean
是相反对应的一组注解,看注解名称也可以看出来。
前者是判断存在某个bean
,后者是判断是否确实某个bean
。
先来看看他们的使用,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @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
对象,还可以传其他的属性来进行确定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| @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 {};
}
|
3)@ConditionalOnProperty
看名字也就能看的出来,是指从配置文件加载,用来进行条件判断。使用如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 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()); }
}
|
还是点入进去看看该注解的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @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;
}
|
还有一段不好翻译,给予截图吧,指的是havingValue
属性的使用。
这些比较特殊,不同的属性值和不同的havingValue
组合,可以得到什么样的结果。
4)@ConditionalOnClass
@ConditionalOnClass
和@ConditionalOnMissingClass
也是一组相对的注解,他们的功能是判断是否拥有java
类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @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
,所以结果如下
5)@ConditionalOnWebApplication
@ConditionalOnWebApplication
与@ConditionalOnNotWebApplication
是一组相对的注解,指的判断是否是web
环境应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 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()); }
}
|
6)@ConditionalOnJava
@ConditionalOnJava
用来判断当前java
的版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @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()); }
}
|
看下注解的源码,比较简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnJavaCondition.class) public @interface ConditionalOnJava {
Range range() default Range.EQUAL_OR_NEWER;
JavaVersion value();
enum Range {
EQUAL_OR_NEWER,
OLDER_THAN
}
}
|
7) @ConditionalOnExpression
使用Spring
表达式来进行判断,也就是SpEL
表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @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()); }
}
|
8)@ConditionalOnResource
@ConditionalOnResource
是否有指定的静态资源,示例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @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()); }
}
|
9)@ConditionalOnSingleCandidate
@ConditionalOnSingleCandidate
,判断指定的类型是否只有一个bean
,示例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @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 过滤器 |
我是半月,祝你幸福!!!