關於@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更加安全,不易出錯。