Spring Boot 條件注解
?SpringBoot條件注解@Conditional,可用于根據(jù)某個(gè)特定的條件來(lái)判斷是否需要?jiǎng)?chuàng)建某個(gè)特定的Bean。SpringBoot自動(dòng)配置功能里面就大量的使用了條件注解。接下來(lái)我們就對(duì)@Conditional的使用做一個(gè)簡(jiǎn)單的介紹。
? ? ? ?@Conditional注解需要和Condition接口搭配一起使用。通過(guò)對(duì)應(yīng)Condition接口來(lái)告知是否滿足匹配條件。
@Target({ElementType.TYPE,?ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public?@interface?Conditional?{
????/**
?????*?所有用于匹配的Condition接口(實(shí)現(xiàn)該接口的類),只有這些類都返回true才認(rèn)為是滿足條件
?????*/
????Class?extends?Condition>[]?value();
}
? ? ? ?@Conditional注解可以添加在@Configuration、@Component、@Service等修飾的類上用于控制對(duì)應(yīng)的Bean是否需要?jiǎng)?chuàng)建,或者添加在@Bean修飾的方法上用于控制方法對(duì)應(yīng)的Bean是否需要?jiǎng)?chuàng)建。
@Conditional添加在@Configuration修飾的類上,用于控制該類和該類里面所有添加的@Bean方法對(duì)應(yīng)的Bean是否需要?jiǎng)?chuàng)建。
一 @Conditional擴(kuò)展注解
? ? ? ?為了方便我們的使用Spring Boot對(duì)@Conditional條件注解做了一些擴(kuò)展,提供了一些很實(shí)用的擴(kuò)展性條件注解。
| 條件注解 | Condition處理類 | 實(shí)例 | 解釋 |
|---|---|---|---|
| @ConditionalOnBean | OnBeanCondition | @ConditionalOnBean(DataSource.class) | Spring容器中不存在對(duì)應(yīng)的實(shí)例生效 |
| @ConditionalOnMissingBean | OnBeanCondition | @ConditionalOnMissingBean(name = "redisTemplate") | Spring容器中不存在對(duì)應(yīng)的實(shí)例生效 |
| @ConditionalOnSingleCandidate | OnBeanCondition | @ConditionalOnSingleCandidate(FilteringNotifier.class) | Spring容器中是否存在且只存在一個(gè)對(duì)應(yīng)的實(shí)例,或者雖然有多個(gè)但 是指定首選的Bean生效 |
| @ConditionalOnClass | OnClassCondition | @ConditionalOnClass(RedisOperations.class) | 類加載器中存在對(duì)應(yīng)的類生效 |
| @ConditionalOnMissingClass | OnClassCondition | @ConditionalOnMissingClass(RedisOperations.class) | 類加載器中不存在對(duì)應(yīng)的類生效 |
| @ConditionalOnExpression | OnExpressionCondition | @ConditionalOnExpression(“’${server.host}’==’localhost’”) | 判斷SpEL 表達(dá)式成立生效 |
| @ConditionalOnJava | OnJavaCondition | @ConditionalOnJava(JavaVersion.EIGHT) | 指定Java版本符合要求生效 |
| @ConditionalOnProperty | OnPropertyCondition | @ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true) | 應(yīng)用環(huán)境中的屬性滿足條件生效 |
| @ConditionalOnResource | OnResourceCondition | @ConditionalOnResource(resources=”mybatis.xml”) | 存在指定的資源文件生效 |
| @ConditionalOnWebApplication | OnWebApplicationCondition | 當(dāng)前應(yīng)用是Web應(yīng)用生效 | |
| @ConditionalOnNotWebApplication | OnWebApplicationCondition | 當(dāng)前應(yīng)用不是Web應(yīng)用生效 |
? ? ? ?上面的擴(kuò)展注解我們可以簡(jiǎn)單的分為以下幾類:
Bean作為條件:@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnSingleCandidate。
類作為條件:@ConditionalOnClass、@ConditionalOnMissingClass。
SpEL表達(dá)式作為條件:@ConditionalOnExpression。
JAVA版本作為條件: @ConditionalOnJava
配置屬性作為條件:@ConditionalOnProperty。
資源文件作為條件:@ConditionalOnResource。
是否Web應(yīng)用作為判斷條件:@ConditionalOnWebApplication、@ConditionalOnNotWebApplication。
1.1 Bean作為條件
1.1.1 @ConditionalOnBean
? ? ? ?@ConditionalOnBean對(duì)應(yīng)的Condition處理類是OnBeanCondition。如果Spring容器里面存在指定的Bean則生效。
@ConditionalOnBean配置參數(shù)
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public?@interface?ConditionalOnBean?{
????/**
?????*?需要作為條件的類的Class對(duì)象數(shù)組
?????*/
????Class>[]?value()?default?{};
????/**
?????*?需要作為條件的類的Name,?Class.getName()
?????*/
????String[]?type()?default?{};
????/**
?????*?(用于指定注解修飾的Bean)條件所需的注解類
?????*/
????Class?extends?Annotation>[]?annotation()?default?{};
????/**
?????*?Spring容器中Bean的名字
?????*/
????String[]?name()?default?{};
????/**
?????*?搜索容器層級(jí),當(dāng)前容器,父容器
?????*/
????SearchStrategy?search()?default?SearchStrategy.ALL;
????/**
?????*?可能在其泛型參數(shù)中包含指定Bean類型的其他類
?????*/
????Class>[]?parameterizedContainer()?default?{};
}
1.1.2 @ConditionalOnMissingBean
? ? ? ?@ConditionalOnMissingBean對(duì)應(yīng)的Condition實(shí)現(xiàn)類是OnBeanCondition。如果Spring容器里面不存在指定的Bean則生效。
@ConditionalOnMissingBean配置參數(shù)
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public?@interface?ConditionalOnMissingBean?{
????/**
?????*?需要作為條件的類的Class對(duì)象數(shù)組
?????*/
????Class>[]?value()?default?{};
????/**
?????*?需要作為條件的類的Name,?Class.getName()
?????*/
????String[]?type()?default?{};
????/**
?????*?匹配Bean的時(shí)候需要忽視的Class對(duì)象數(shù)組,一般是父類
?????*?@ConditionalOnMissingBean(value?=?JdbcFactory.class,?ignored?=?MySqlDefaultFactory.class)
?????*/
????Class>[]?ignored()?default?{};
????/**
?????*?匹配Bean的時(shí)候需要忽視的類的Name,?Class.getName()
?????*/
????String[]?ignoredType()?default?{};
????/**
?????*?(用于指定注解修飾的Bean)條件所需的注解類
?????*/
????Class?extends?Annotation>[]?annotation()?default?{};
????/**
?????*?Spring容器中Bean的名字
?????*/
????String[]?name()?default?{};
????/**
?????*?搜索容器層級(jí),當(dāng)前容器,父容器
?????*/
????SearchStrategy?search()?default?SearchStrategy.ALL;
????/**
?????*?可能在其泛型參數(shù)中包含指定Bean類型的其他類
?????*/
????Class>[]?parameterizedContainer()?default?{};
}? ? ? ?比如如下的實(shí)例,當(dāng)容器里面不存在redisTemplate對(duì)應(yīng)的Bean的時(shí)候,就會(huì)創(chuàng)建一個(gè)RedisTemplate添加到容器里面去。
@Bean
????@ConditionalOnMissingBean(name?=?"redisTemplate")
????public?RedisTemplate<Object,?Object>?redisTemplate(RedisConnectionFactory?redisConnectionFactory)
????????????throws?UnknownHostException?{
????????RedisTemplate<Object,?Object>?template?=?new?RedisTemplate<>();
????????template.setConnectionFactory(redisConnectionFactory);
????????return?template;
????}
1.1.3 @ConditionalOnSingleCandidate
? ? ? ?@ConditionalOnSingleCandidate對(duì)應(yīng)的Condition處理類是OnBeanCondition。如果當(dāng)指定Bean在容器中只有一個(gè),或者雖然有多個(gè)但是指定首選Bean的時(shí)候則生效。
@ConditionalOnSingleCandidate配置參數(shù)
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public?@interface?ConditionalOnSingleCandidate?{
????/**
?????*?需要作為條件的類的Class對(duì)象
?????*/
????Class>?value()?default?Object.class;
????/**
?????*?需要作為條件的類的Name,?Class.getName()
?????*/
????String?type()?default?"";
????/**
?????*?搜索容器層級(jí),當(dāng)前容器,父容器
?????*/
????SearchStrategy?search()?default?SearchStrategy.ALL;
}1.2 類作為條件
1.2.1 @ConditionalOnClass
? ? ? ?@ConditionalOnClass對(duì)應(yīng)的Condition處理類是OnClassCondition。如果當(dāng)前類路徑下面有指定的類的時(shí)候則生效。
@ConditionalOnClass配置屬性介紹
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public?@interface?ConditionalOnClass?{
????/**
?????*?需要作為條件的類的Class對(duì)象數(shù)組
?????*/
????Class>[]?value()?default?{};
????/**
?????*?需要作為條件的類的Name,?Class.getName()
?????*/
????String[]?name()?default?{};
}
1.2.2 @ConditionalOnMissingClass
? ? ? ?@ConditionalOnMissingClass對(duì)應(yīng)的Condition處理類是OnClassCondition。如果當(dāng)前類路徑下面沒(méi)有指定的類的時(shí)候則生效。
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public?@interface?ConditionalOnMissingClass?{
????/**
?????*?需要作為條件的類的Name,?Class.getName()
?????*/
????String[]?value()?default?{};
}
1.3 ?SpEL表達(dá)式作為條件
? ? ? ?@ConditionalOnExpression對(duì)應(yīng)的Condition處理類是OnExpressionCondition。只有當(dāng)SpEL表達(dá)式滿足條件的時(shí)候則生效。
@Retention(RetentionPolicy.RUNTIME)
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Documented
@Conditional(OnExpressionCondition.class)
public?@interface?ConditionalOnExpression?{
????/**
?????*?要作為條件的SpEL表達(dá)式
?????*/
????String?value()?default?"true";
}? ? ? ?例如@ConditionalOnExpression("${test.enabled:true}"),只有當(dāng)配置文件里面存在test.enabled: true的時(shí)候則生效。
更加詳細(xì)的用法可以去看下SpEL表達(dá)式的使用。
1.4 ?JAVA版本作為判斷條件
? ? ? ?@ConditionalOnJava對(duì)應(yīng)的Condition處理類是OnJavaCondition。只有當(dāng)指定的JAVA版本條件滿足的時(shí)候,才會(huì)創(chuàng)建對(duì)應(yīng)的Bean。
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnJavaCondition.class)
public?@interface?ConditionalOnJava?{
????/**
?????*?比較方式,Range.EQUAL_OR_NEWER:當(dāng)前版本等于或高于、Range.OLDER_THAN:當(dāng)前版本老于,越早的版本越老
?????*/
????Range?range()?default?Range.EQUAL_OR_NEWER;
????/**
?????*?指定JAVA版本
?????*/
????JavaVersion?value();
????/**
?????*?Range?options.
?????*/
????enum?Range?{
????????/**
?????????*?Equal?to,?or?newer?than?the?specified?{@link?JavaVersion}.
?????????*/
????????EQUAL_OR_NEWER,
????????/**
?????????*?Older?than?the?specified?{@link?JavaVersion}.
?????????*/
????????OLDER_THAN
????}
}
1.5 配置屬性作為判斷條件
? ? ? ?@ConditionalOnProperty對(duì)應(yīng)的Condition實(shí)現(xiàn)類OnPropertyCondition。只有當(dāng)對(duì)應(yīng)的配置屬性和給定條件的值相等的時(shí)候則生效。
@Retention(RetentionPolicy.RUNTIME)
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Documented
@Conditional(OnPropertyCondition.class)
public?@interface?ConditionalOnProperty?{
????/**
?????*?對(duì)應(yīng)property名稱的值
?????*/
????String[]?value()?default?{};
????String[]?name()?default?{};
????/**
?????*?property名稱的前綴,可有可無(wú)
?????*/
????String?prefix()?default?"";
????/**
?????*?與name組合使用,比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置
?????*/
????String?havingValue()?default?"";
????/**
?????*?缺少該property時(shí)是否可以加載。如果為true,沒(méi)有該property也會(huì)正常加載;反之報(bào)錯(cuò)
?????*/
????boolean?matchIfMissing()?default?false;
}
? ? ? ?@ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”)表示當(dāng)配置文件里面spring.aop.auto=true的時(shí)候才會(huì)加載對(duì)應(yīng)的Bean。
1.6 資源文件是否存在作為判斷條件
? ? ? ?@ConditionalOnResource對(duì)應(yīng)的Condition處理類OnResourceCondition。只有當(dāng)指定的資源文件出現(xiàn)在classpath中則生效。
@ConditionalOnResource配置屬性
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnResourceCondition.class)
public?@interface?ConditionalOnResource?{
????/**
?????*?要作為判斷條件的資源文件名稱??@ConditionalOnResource(resources=”mybatis.xml”)
?????*/
????String[]?resources()?default?{};
}
1.7 是否Web應(yīng)用作為判斷條件
1.7.1 @ConditionalOnWebApplication
? ? ? ?@ConditionalOnWebApplication對(duì)應(yīng)的Condition處理類是OnWebApplicationCondition。只有當(dāng)當(dāng)前項(xiàng)目是Web項(xiàng)目的時(shí)候則生效。
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public?@interface?ConditionalOnWebApplication?{
????/**
?????*?需要作為條件的Web應(yīng)用程序的必需類型
?????*/
????Type?type()?default?Type.ANY;
????/**
?????*?Available?application?types.
?????*/
????enum?Type?{
????????/**
?????????*?任何web應(yīng)用都將匹配
?????????*/
????????ANY,
????????/**
?????????*?僅基于servlet的Web應(yīng)用程序?qū)⑵ヅ?br>?????????*/
????????SERVLET,
????????/**
?????????*?僅基于反應(yīng)式的Web應(yīng)用程序?qū)⑵ヅ?br>?????????*/
????????REACTIVE
????}
}1.7.2 @ConditionalOnNotWebApplication
? ? ? ?@ConditionalOnNotWebApplication對(duì)應(yīng)的Condition處理類是OnWebApplicationCondition。只有當(dāng)當(dāng)前項(xiàng)目不是Web項(xiàng)目的時(shí)候則生效。
@Target({?ElementType.TYPE,?ElementType.METHOD?})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public?@interface?ConditionalOnNotWebApplication?{
}
二 @Conditional自定義
? ? ? ?上面介紹每個(gè)擴(kuò)展注解的時(shí)候都特意提到了每個(gè)注解對(duì)應(yīng)的Condition實(shí)現(xiàn)類。其實(shí)我們可以仿照這些Condition實(shí)現(xiàn)類來(lái)實(shí)現(xiàn)我們自己的@Conditional注解。下面我們同個(gè)兩個(gè)簡(jiǎn)單的實(shí)例來(lái)看下怎么實(shí)現(xiàn)自己的@Conditional擴(kuò)展注解。
2.1 判斷是否配置指定屬性
? ? ? ?注意,和@ConditionalOnProperty不一樣哦,@ConditionalOnProperty是判斷是否有屬性并且判斷值是否等于我們指定的值。我們要實(shí)現(xiàn)的注解只判斷有沒(méi)有配置屬性,不管屬性對(duì)應(yīng)的值。
? ? ? ?擴(kuò)展注解ConditionalOnPropertyExist。指定我們的Condition實(shí)現(xiàn)類OnPropertyExistCondition。并且指定兩個(gè)參數(shù)。一個(gè)是參數(shù)name用于指定屬性。另一個(gè)參數(shù)exist用于指定是判斷存在還是不存在。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,?ElementType.METHOD})
@Documented
@Conditional(OnPropertyExistCondition.class)
public?@interface?ConditionalOnPropertyExist?{
????/**
?????*?配置文件里面對(duì)應(yīng)的key
?????*/
????String?name()?default?"";
????/**
?????*?是否有配置的時(shí)候判斷通過(guò)
?????*/
????boolean?exist()?default?true;
}
? ? ? ?OnPropertyExistCondition類就是簡(jiǎn)單的判斷下屬性存在與否。
public?class?OnPropertyExistCondition?implements?Condition?{
????@Override
????public?boolean?matches(ConditionContext?conditionContext,?AnnotatedTypeMetadata?annotatedTypeMetadata)?{
????????Map<String,?Object>?annotationAttributes?=?annotatedTypeMetadata.getAnnotationAttributes(ConditionalOnPropertyExist.class.getName());
????????if?(annotationAttributes?==?null)?{
????????????return?false;
????????}
????????String?propertyName?=?(String)?annotationAttributes.get("name");
????????boolean?values?=?(boolean)?annotationAttributes.get("exist");
????????String?propertyValue?=?conditionContext.getEnvironment().getProperty(propertyName);
????????if(values)?{
????????????return?!StringUtils.isEmpty(propertyValue);
????????}?else?{
????????????return?StringUtils.isEmpty(propertyValue);
????????}
????}
}
2.1 判斷是否配置指定屬性
? ? ? ?我們簡(jiǎn)單實(shí)現(xiàn)這樣一個(gè)功能,根據(jù)指定的系統(tǒng)加載不同的Bean。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,?ElementType.METHOD})
@Documented
@Conditional(OnSystemCondition.class)
public?@interface?ConditionalOnSystem?{
????/**
?????*?指定系統(tǒng)
?????*/
????SystemType?type()?default?SystemType.WINDOWS;
????/**
?????*?系統(tǒng)類型
?????*/
????enum?SystemType?{
????????/**
?????????*?windows系統(tǒng)
?????????*/
????????WINDOWS,
????????/**
?????????*?linux系統(tǒng)
?????????*/
????????LINUX,
????????/**
?????????*?mac系統(tǒng)
?????????*/
????????MAC
????}
}
public?class?OnSystemCondition?implements?Condition?{
????@Override
????public?boolean?matches(ConditionContext?context,?AnnotatedTypeMetadata?metadata)?{
????????Map<String,?Object>?annotationAttributes?=?metadata.getAnnotationAttributes(ConditionalOnSystem.class.getName());
????????if?(annotationAttributes?==?null)?{
????????????return?false;
????????}
????????ConditionalOnSystem.SystemType?systemType?=?(ConditionalOnSystem.SystemType)?annotationAttributes.get("type");
????????switch?(systemType)?{
????????????case?WINDOWS:
????????????????return?context.getEnvironment().getProperty("os.name").contains("Windows");
????????????case?LINUX:
????????????????return?context.getEnvironment().getProperty("os.name").contains("Linux?");
????????????case?MAC:
????????????????return?context.getEnvironment().getProperty("os.name").contains("Mac?");
????????}
????????return?false;
????}
}
作者:tuacy
source:?//www.jianshu.com/p/e21b9079a23c

喜歡,在看
