Spring框架中的@Conditional系列注解詳解
1 @Contidional 介紹
Conditional 是由SpringFramework提供的一個注解,位于 org.springframework.context.annotation 包內(nèi),定義如下。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
Class<? extends Condition>[] value();
}SpringBoot 模塊大量的使用@Conditional 注釋,我們可以將Spring的@Conditional注解用于以下場景:
- 可以作為類級別的注解直接或者間接的與@Component相關(guān)聯(lián),包括@Configuration類;
- 可以作為元注解,用于自動編寫構(gòu)造性注解;
- 作為方法級別的注解,作用在任何@Bean方法上。
1.1 Condition 接口
我們需要一個類實現(xiàn)Spring提供的Condition接口,它會匹配@Conditional所符合的方法,然后我們可以使用我們在@Conditional注解中定義的類來檢查。
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}1.2 Spring @Conditional注解實例
作用在方法上
先來看一個簡單一些的示例,我們假設(shè)有三個角色老師Teacher、學生Student和父母Parent,三種環(huán)境Linux、Windows和MacOSX,如果是Linux環(huán)境,就注冊Teacher,如果是Windows環(huán)境就注冊Parent,如果是Mac 環(huán)境就注冊Student。代碼示例如下:
首先創(chuàng)建Teacher和Student對象,沒有任何的屬性和方法,只是一個空類
//如果當前工程運行在Windows系統(tǒng)下,就注冊Student
public class Student {}
//如果當前工程運行在Linux系統(tǒng)下,就注冊Teacher
public class Teacher {}
// 如果是Mac OSX 系統(tǒng),就注冊Parent
public class Parent {}創(chuàng)建一個LinuxCondition和一個WindowsCondition,LinuxCondition能夠匹配Linux環(huán)境,WindowsCondition能夠匹配Windows環(huán)境,MacOSX 系統(tǒng)匹配mac環(huán)境。
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 獲取系統(tǒng)環(huán)境的屬性
String systemName = context.getEnvironment().getProperty("os.name");
if(systemName.contains("Linux")){
return true;
}
return false;
}
}
//自定義一個判斷條件
public class WindowsCondition implements Condition {
/*
* ConditionContext context: spring容器上下文環(huán)境
* 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;
}
}下面來新建匹配注冊環(huán)境,如果系統(tǒng)是Linux環(huán)境,就注冊Teacher,如果系統(tǒng)是Windows,就注冊Parent,如果是Mac 系統(tǒng),就注冊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 被輸出到控制臺,也就是說,我當前所用的系統(tǒng)環(huán)境是MacOSX環(huán)境,所以注冊的是OSXCondition,也就是student的bean。
手動設(shè)置系統(tǒng)環(huán)境
也可以進行手動修改vm.options,把當前的系統(tǒng)環(huán)境變?yōu)長inux 或者Windows,以Idea為例:

在Edit Configurations中找到vm.options 選項,把系統(tǒng)環(huán)境改為 Linux,如下:

然后重新啟動測試,發(fā)現(xiàn)Teacher 被注入進來了,修改當前環(huán)境為Windows,觀察Parent也被注入進來并輸出了。
作用在類上
@Conditional 注解可以作用在類上,表示此類下面所有的bean滿足條件后都可以進行注入,通常與@Configuration注解一起使用。
新建一個AppClassConfig,在類上標注@Conditional()注解,并配置相關(guān)bean,如下:
@Conditional(value = OsxCondition.class)
上文表示如果是OsxCondition.class 的話,就注冊student、teacher、parent
測試類不用修改,直接用原測試類進行測試,發(fā)現(xiàn)student、 teacher、 parent 都被注冊進來了
多個條件類
因為@Conditional注解的value 方法默認傳遞一個數(shù)組,所以可以接受多個condition,為了測試如下情況,
新建一個 TestCondition類,如下:
// 單純?yōu)榱藴y試
public class TestCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 返回false,表示不匹配
return false;
}
}修改一下AppClassConfig
@Conditional(value = {OsxCondition.class,TestCondition.class})也就是給@Conditional 多加了一個參數(shù) TestCondition.class
啟動之前的測試類,發(fā)現(xiàn)上述的bean都沒有注入,也就是說,只有在滿足OsxCondition.class 和 TestCondition.class 都為true的情況下,才會注入對應(yīng)的bean,修改TestCondition.class的matches方法的返回值為true,重新觀察返回結(jié)果,發(fā)現(xiàn)上述bean都被注入了。
1.3 @Conditional 與@Profile 的對比
@Spring3.0 也有一些和@Conditional 相似的注解,它們是Spring SPEL 表達式和Spring Profiles 注解 Spring4.0的@Conditional 注解要比@Profile 注解更加高級。
@Profile 注解用來加載應(yīng)用程序的環(huán)境。@Profile注解僅限于根據(jù)預定義屬性編寫條件檢查。 @Conditional注釋則沒有此限制。
Spring中的@Profile 和 @Conditional 注解用來檢查"If…then…else"的語義。然而,Spring4 @Conditional是@Profile 注解的更通用法。
- Spring 3中的 @Profiles僅用于編寫基于Environment變量的條件檢查。 配置文件可用于基于環(huán)境加載應(yīng)用程序配置。
- Spring 4 @Conditional注解允許開發(fā)人員為條件檢查定義用戶定義的策略。@Conditional可用于條件bean注冊。
2 Spring boot 擴展
? SpringBoot的spring-boot-autoconfigure模塊也提供了Conditional系列的相關(guān)注解,這些注解能幫助開發(fā)者根據(jù)一定的條件去裝載需要的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
* 如果沒有就創(chuàng)建
* @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 | 當未找到對應(yīng)的配置時是否匹配,默認為false, 如果為true,沒有找到配置,那么也匹配。 |
? 使用場景,例如在指定數(shù)據(jù)源時,指定datasource的type。
例如包含如下配置使用Hikari數(shù)據(jù)源。
spring.datasource.type=com.zaxxer.hikari.HikariDataSource

? 在使用時,一般設(shè)置matchIfMissing=false, 這樣條件沒有匹配上的話會Spring在掃描bean時會自動跳過該配置類。
? 也可以設(shè)定matchIfMissing=true,這種場景例如緩存,我們可以這樣配置默認是開啟緩存的。
@ConditionalOnProperty(name={cache.effect},marchIfMissing=true)
public class CacheAutoConfiguration{
// ...
}? 如果在application.properties里配置cache.effect=false, 那么該配置類就會跳過,這樣配置就能使緩存不生效。
到此這篇關(guān)于Spring框架中的@Conditional系列注解詳解的文章就介紹到這了,更多相關(guān)Spring的@Conditional注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中HttpServletResponse響應(yīng)中文出現(xiàn)亂碼問題
這篇文章主要介紹了Java中HttpServletResponse響應(yīng)中文出現(xiàn)亂碼問題的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-06-06
SpringBootWeb?入門了解?Swagger?的具體使用
這篇文章主要介紹了SpringBootWeb?入門了解?Swagger?的具體使用,Swagger?框架可以根據(jù)已經(jīng)實現(xiàn)的方法或者類,通過頁面的方式直觀清晰的查看或者進行測試該方法,需要的朋友可以參考下2024-08-08
Spring整合Quartz實現(xiàn)定時任務(wù)調(diào)度的方法
下面小編就為大家?guī)硪黄猄pring整合Quartz實現(xiàn)定時任務(wù)調(diào)度的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11
解決springboot 無法配置多個靜態(tài)路徑的問題
這篇文章主要介紹了解決springboot 無法配置多個靜態(tài)路徑的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08

