這篇文章介紹Spring 4的@Conditional注解。
一、在Spring的早期版本你可以通過以下方法來處理條件問題
- 3.1之前的版本,使用Spring Expression Language(SPEL)。
- 3.1版本有個新特性叫profile,用來解決條件問題。
1.1、Spring Expression Language(SPEL)
SPEL有一個三元運算符(if-then-else)可以在配置文件中當作條件語句,如下:
<bean id="flag"> <constructor-arg value="#{systemProperties['system.propery.flag'] ?: false }" /> </bean> <bean id="testBean"> <property name="prop" value="#{ flag ? 'yes' : 'no' }"/> </bean>
testBean的prop動態依賴於flag的值。
1.2、使用Profile
<!-- 如果沒有設置profile,default.xml將被加載 --> <!-- 必須放置在配置文件的最底下,后面再也沒有bean的定義 --> <beans profile="default"> <import resource="classpath:default.xml" /> </beans> <!-- some other profile --> <beans profile="otherProfile"> <import resource="classpath:other-profile.xml" /> </beans>
二、使用@Conditional
官方文檔定義:“Indicates that a component is only eligible for registration when all specified conditions match”,意思是只有滿足一些列條件之后創建一個bean。
除了自己自定義Condition之外,Spring還提供了很多Condition給我們用
@ConditionalOnClass : classpath中存在該類時起效
@ConditionalOnMissingClass : classpath中不存在該類時起效
@ConditionalOnBean : DI容器中存在該類型Bean時起效
@ConditionalOnMissingBean : DI容器中不存在該類型Bean時起效
@ConditionalOnSingleCandidate : DI容器中該類型Bean只有一個或@Primary的只有一個時起效
@ConditionalOnExpression : SpEL表達式結果為true時
@ConditionalOnProperty : 參數設置或者值一致時起效
@ConditionalOnResource : 指定的文件存在時起效
@ConditionalOnJndi : 指定的JNDI存在時起效
@ConditionalOnJava : 指定的Java版本存在時起效
@ConditionalOnWebApplication : Web應用環境下起效
@ConditionalOnNotWebApplication : 非Web應用環境下起效
@Conditional定義
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE, ElementType.METHOD) public @interface Conditional{ Class<? extends Condition>[] value(); }
@Conditional注解主要用在以下位置:
-
類級別可以放在注標識有@Component(包含@Configuration)的類上
- 作為一個meta-annotation,組成自定義注解
- 方法級別可以放在標識由@Bean的方法上
如果一個@Configuration的類標記了@Conditional,所有標識了@Bean的方法和@Import注解導入的相關類將遵從這些條件。
condition接口定義如下:
public interface Condition { /** * Determine if the condition matches. * @param context the condition context * @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class} * or {@link org.springframework.core.type.MethodMetadata method} being checked. * @return {@code true} if the condition matches and the component can be registered * or {@code false} to veto registration. */ boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
示例1:下面看一個例子:
結果:
示例2:@ConditionalOnProperty來控制Configuration是否生效
Spring Boot通過@ConditionalOnProperty來控制Configuration是否生效
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { String[] value() default {}; //數組,獲取對應property名稱的值,與name不可同時使用 String prefix() default "";//property名稱的前綴,可有可無 String[] name() default {};//數組,property完整名稱或部分名稱(可與prefix組合使用,組成完整的property名稱),與value不可同時使用 String havingValue() default "";//可與name組合使用,比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置 boolean matchIfMissing() default false;//缺少該property時是否可以加載。如果為true,沒有該property也會正常加載;反之報錯 boolean relaxedNames() default true;//是否可以松散匹配,至今不知道怎么使用的 } }
通過其兩個屬性name以及havingValue來實現的,其中name用來從application.properties中讀取某個屬性值。
如果該值為空,則返回false;
如果值不為空,則將該值與havingValue指定的值進行比較,如果一樣則返回true;否則返回false。
如果返回值為false,則該configuration不生效;為true則生效。
@Configuration //在application.properties配置"mf.assert",對應的值為true @ConditionalOnProperty(prefix="mf",name = "assert", havingValue = "true") public class AssertConfig { @Autowired private HelloServiceProperties helloServiceProperties; @Bean public HelloService helloService(){ HelloService helloService = new HelloService(); helloService.setMsg(helloServiceProperties.getMsg()); return helloService; } }
s