spring4.0之五:@Conditional在滿足特定條件下,才會實例化對象


這篇文章介紹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:下面看一個例子:

package com.dxz.demo.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class LinuxCondition implements Condition {

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Linux");
    }
}

package com.dxz.demo.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class WindowsCondition implements Condition {

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment().getProperty("os.name").contains("Windows");
    }
}

我們有兩個類LinuxCondition 和WindowsCondition 。兩個類都實現了Condtin接口,重載的方法返回一個基於操作系統類型的布爾值。

下面我們定義兩個bean,一個符合條件另外一個不符合條件:

package com.dxz.demo.condition;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfiguration {

    @Bean(name = "emailerService")
    @Conditional(WindowsCondition.class)
    public EmailService windowsEmailerService() {
        return new WindowsEmailService();
    }

    @Bean(name = "emailerService")
    @Conditional(LinuxCondition.class)
    public EmailService linuxEmailerService() {
        return new LinuxEmailService();
    }
}

 當符合某一個條件的時候,這里的@Bean才會被初始化。 

測試相關其它類:

package com.dxz.demo.condition;

public interface EmailService {

    public void sendEmail();
}

package com.dxz.demo.condition;

public class WindowsEmailService implements EmailService {

    public void sendEmail() {
        System.out.println("send windows email");
    }

}

package com.dxz.demo.condition;

public class LinuxEmailService implements EmailService {

    public void sendEmail() {
        System.out.println("send linux email");

    }

}


package com.dxz.demo.condition;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class ConditionClient {

    @Autowired
    private EmailService emailService;

    @Scheduled(initialDelay = 3000, fixedDelay = 10000)
    public void test() {
        emailService.sendEmail();
    }
}

結果:

 示例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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM