1. 程式人生 > >關於@Profile、@Conditional、@Primary、@Qualifier及@Scope等實現高階裝配的spring註解

關於@Profile、@Conditional、@Primary、@Qualifier及@Scope等實現高階裝配的spring註解

1、關於@Profile註解的介紹

@Profile註解主要用在針對不同環境而條件選擇的注入bean

在開發過程中由於環境的不同,我們可能在針對某些功能,需要開發不同的實現,然而在某種環境中,只能啟用其中一種實現,其他的實現處於不啟用的狀態。這個時候我們在需要建立的bean上新增@Profile註解,如@Profile("dev"),"dev"是啟用的標識。當此沒有註解被啟用時,該註解下的所有@Bean都會被忽略掉,啟用則相反。@Profile註解在spring3.1只能在類級別上註解,spring3.2開始可以在方法級別註解。

如:

@Profile("dev")
public class TestProfile{
    
    @Bean
    public TofuCai setTofuCai(){
        return new TofuCai;
    }

}

@profile的啟用依賴兩個獨立的屬性:spring.profiles.active和spring.profiles.default。

有多種方式來設定這兩個屬性

    作為DispatcherServlet的初始化引數;

    作為Web應用的上下文引數;

    作為JNDI的條目;

    作為環境變數;

    作為JVM的系統屬性;

    在整合車市類上,使用@ActiveProfiles註解設定

如為上下文設定預設的profile:

<context-param>
        <param-name>spring.profiles.default</param-name>
        <param-value>dev</param-value>
  </context-param>

注意:可以同時啟用多個profile,通過逗號分隔來實現

 

2、@Conditional條件化的bean

當我們需要將某個bean注入到Spring中時,需要考慮到,這個bean只有在滿足某些條件才去建立時,這個時候我們在這個bean的類或法返回物件的方法上,新增@Conditional註解。

例如:

@Bean
@Conditional(TofuCaiCondition.class)
public TofuCai tofuCaiBean(){
    return new TofuCai();
}

我們在建立TofuCai這個Bean時,添加了建立的條件。

再新增這個註解後,我們需要實現Condition這個介面,只需提供matches的實現就好。

例如:

public class TofuCaiCondition implements Condition{
    public boolean matches(
         ConditionContext context, AnnotatedTypeMetadata metadata){
        Environment env = context.getEnvironment();
        return env.containsProperty("tofu");    
    }
}

其實@Profile的內部實現,也是使用了@Conditional這個註解

如下是spring 4中 @Profile的實現:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profilr{
    String[] value();
}

3、@Qualifier 處理裝配的歧義性

先解釋下何為歧義性:當使用@Autowired或者其他註解(如:@Inject)進行裝配的時候,由於該引用下存在多個實現,spring不知曉從眾多實現中取其中的哪一個進行裝配。這我們稱之為歧義性。而@Qualifier則能很好解決裝配的歧義性。

在這之前我先說下@Primary這個註解。

當我們在某個需要注入的bean使用這個註解的時候,當發生歧義性的時候,spring就會裝配有@Primary這個實現。需要注意的是,在多個實現中,只能最多隻有一個實現可以使用這個註解,否則還是會產生歧義性。

這個時候,就輪到@Qualifier這個註解大顯身手了。

在裝配的額外增加@Qualifier("tofu")這個註解,就意味著spring會直接尋找Tofu這個bean。當然你可以在注入這個bean指定下ID,同樣加上@Qualifier這個註解。例如:

@Component
@Qualifier("cai")
public class Tofu implement TofuCai{
    
}

 注意:@Qualifier註解可以跟注入bean的註解一起使用,比如@Bean也是可以結合使用的,注意其不同組合之間的含義。在與注入bean的相關注解使用時,是代表限定符的意思,在裝配的時候某種角度來說,是引用的指向的意思。

@Autowired
@Qualifier("cai")
public void setTofuCai(TofuCai tofuCai){
    this.tofuCai = tofuCai;
}

其實介紹到現在基本上已經很難再存在歧義性的問題了,但是有時候吧,有的人在注入bean的時候,限定符重複使用。這就導致在裝配時,又出現歧義性。而且Java是不允許同一個條目上重複出現相同的註解。因此spring提供終極解決方案,即使用自定義的限定符註解,就是使用者自己定義限定符註解,既可單個使用,也可組合使用。

例如:

@Target({ElementType.CONSTRUCTOR,ElementType.FILED,
        ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cai{}
@Target({ElementType.CONSTRUCTOR,ElementType.FILED,
        ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Tofu{}

就分別定義了@Tofu和@Cai兩個註解,直接在裝配和注入的時候使用這兩個註解就好了,而且既可以單獨使用,也可以組合使用,不必考慮註解的重複。

4、@Scope註解——bean的作用域

先大致介紹下spring定義的一些作用域:

       單例(Singleton):在整個應用中,只建立bean的一個例項。

       原型(Prototype):每次注入或者通過spring應用上下文獲取的時候,都會建立一個新的bean的例項。

       會話(Session):在Web應用中,為每個會話建立一個bean的例項。

       請求(Request):在Web應用中,為每個請求建立一個bean的例項。

在bean的類上使用@Scope註解,例如:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class TofuCai{...}

這裡就將這TofuCai設定成原型作用域。

也可以這麼使用@Scope("prototype"),但是沒有使用SCOPE_PROTOTYPR更加安全,不易出錯。