前言:java
在上一章裝配Bean中,咱們看到了一些最爲核心的bean裝配技術。你可能會發現上一章學到的知識有很大的用處。可是,bean裝配所涉及的領域並不只僅侷限於上一章 所學習到的內容。Spring提供了多種技巧,藉助它們能夠實現更爲高級的bean裝配功能。在本章中,咱們將會深刻介紹一些這樣的高級技術。linux
使用情景:web
在開發軟件的時候,有一個很大的挑戰就是將應用程序從一個環境遷移到另一個環境。開發階段中,某些環境相關作法可能並不適合遷移到生產環境中,甚至即使遷移過去也沒法正常工做。數據庫配置、加密算法以及與外部系統的集成是跨環境部署時會發生變化的幾個典型例子。算法
Spring須要根據環境決定該建立哪一個bean和不建立哪一個bean。不過Spring並非在構建的時候作出這樣的決策,而是等到運行時再來肯定。這樣的結果就是同一個部署單元(可能會是WAR文件)可以適用於全部的環境,沒有必要進行從新構建。spring
使用profile步驟:sql
開發環境和生產環境一般採用不一樣的數據庫鏈接方式,例如在開發環境能夠採用嵌入式,而生產環境中採用jndi鏈接池,因此要根據不一樣環境配置不一樣的bean,Spring中提供了profile來實現動態生成相應的bean:數據庫
嵌入式DataSource:app
它會告訴Spring這個配置類中的bean只有在dev profile激活時纔會建立。若是dev profile沒有激活的話,那麼帶有@Bean註解的方法都會被忽略掉。學習
同時,你可能還須要有一個適用於生產環境的配置,以下所示:測試
在本例中,只有prod profile激活的時候,纔會建立對應的bean;
在Spring 3.1中,只能在類級別上使用@Profile註解。不過,從Spring 3.2開始,你也能夠在方法級別上使用@Profile註解,與@Bean註解一同使用。這樣的話,就能將這兩個bean的聲明放到同一個配置類之中,以下所示:
這裏有個問題須要注意,儘管每一個DataSource bean都被聲明在一個profile中,而且只有當規定的profile激活時,相應的bean纔會被建立,可是可能會有其餘的bean並無聲明在一個給定的profile範圍內。沒有指定profile的bean始終都會被建立,與激活哪一個profile沒有關係。
咱們也能夠經過<beans>元素的profile屬性,在XML中配置profile bean。例如,爲了在XML中定義適用於開發階段的嵌入式數據庫DataSourcebean,咱們能夠建立以下所示的XML文件:
你還能夠在根<beans>元素中嵌套定義<beans>元素,而不是爲每一個環境都建立一個profile XML文件。這可以將全部的profile bean定義放到同一個XML文件中,以下所示:
除了全部的bean定義到了同一個XML文件之中,這種配置方式與定義在單獨的XML文件中的實際效果是同樣的。這裏有三個bean,類型都是javax.sql.DataSource,而且ID都是dataSource。可是在運行時,只會建立一個bean,這取決於處於激活狀態的是哪一個profile。
經過profile標記不一樣的環境,可是如何激活它呢,能夠經過設置spring.profiles.active和spring.profiles.default。若是設置了active,default便失去了做用。若是沒有設置active就會去找default的值。若是兩個都沒有設置,那麼那些定義在profiles的bean都不會生成。
有多種方式來設置這兩個屬性:
以上面的前兩種方式舉例,設置spring.profiles.default的web.xml文件會以下所示:
按照這種方式設置spring.profiles.default,使用開發環境的設置(如嵌入式數據庫)運行代碼,而不須要任何額外的配置。
當應用程序部署到QA、生產或其餘環境之中時,負責部署的人根據狀況使用系統屬性、環境變量或JNDI設置spring.profiles.active便可。當設置spring.profiles.active之後,至於spring.profiles.default置成什麼值就已經無所謂了;系統會優先使用spring.profiles.active中所設置的profile。
補充:在系統的環境變量裏的設置的優先級高於application.properties裏的spring.profiles.active的設置:
使用profile進行測試
當運行集成測試時,一般會但願採用與生產環境(或者是生產環境的部分子集)相同的配置進行測試。可是,若是配置中的bean定義在了profile中,那麼在運行測試時,咱們就須要有一種方式來啓用合適的profile。
Spring提供了@ActiveProfiles註解,咱們可使用它來指定運行測試時要激活哪一個profile。在集成測試時,一般想要激活的是開發環境的profile。例如,下面的測試類片斷展示了使用@ActiveProfiles激活dev profile:
經過活動的profile,咱們能夠得到不一樣的Bean。Spring 4提供了一個更通用的基於條件的Bean的建立方式,即便用@Conditional註解。
@Conditional根據知足某個特定的條件建立一個特定的Bean。好比,當某一個jar包在一個類路徑下時,自動配置一個或者多個Bean。或者只有一個Bean建立時,纔會建立另外一個Bean。總的來講,就是根據特定條件來控制Bean的建立行爲,這樣咱們能夠利用這個特性進行一些自動配置。
下面的示例將以「環境中是否存在magic屬性」做爲條件,咱們將經過實現Condition接口,並重寫其matches方法來構造判斷條件。
能夠看到,@Conditional中給定了一個Class,它指明瞭條件,也就是MagicExistsCondition。
在上面的程序清單中,matches()方法很簡單但功能強大。它經過給定的ConditionContext對象進而獲得Environment對象,並使用這個對象檢查環境中是否存在名爲magic的環境屬性。若是知足這個條件的話,matches()方法就會返回true。全部@Conditional註解上引用MagicExistsCondition的bean都會被建立。若是這個屬性不存在的話,就沒法知足條件,matches()方法會返回false,這些bean都不會被建立。
介紹matches()方法
提供ConditionContext和AnnotatedTypeMetadata對象;
1.一、ConditionContext接口以下:
經過ConditionContext,咱們能夠作到以下幾點:
1.二、AnnotatedTypeMetadata
AnnotatedTypeMetadata則可以讓咱們檢查帶有@Bean註解的方法上還有什麼其餘的註解。像ConditionContext同樣,AnnotatedTypeMetadata也是一個接口。它以下所示:
藉助isAnnotated()方法,咱們可以判斷帶有@Bean註解的方法是否是還有其餘特定的註解。藉助其餘的那些方法,咱們可以檢查@Bean註解的方法上其餘註解的屬性。
從Spring 4開始,@Profile註解進行了重構,使其基於@Conditional和Condition實現。咱們來看一下在Spring 4中,@Profile是如何實現的。
@Profile註解以下所示:
注意:@Profile自己也使用了@Conditional註解,而且引用ProfileCondition做爲Condition實現了Condition接口,而且在作出決策的過程當中,考慮到了ConditionContext和AnnotatedTypeMetadata中的多個因素。
條件:ProfileCondition檢查某個bean profile是否可用
咱們能夠看到,ProfileCondition經過AnnotatedTypeMetadata獲得了用於@Profile註解的全部屬性。藉助該信息,它會明確地檢查value屬性,該屬性包含了bean的profile名稱。而後,它根據經過ConditionContext獲得的Environment來檢查[藉助acceptsProfiles()方法]該profile是否處於激活狀態。