使用Spring profile 能夠設定在不一樣的環境中啓用不一樣的bean.因爲環境的不一樣,數據庫配置,加密算法以及與外部系統的集成可能會有不一樣的表現,特別是數據庫,通常開發環境,QA環境,預發佈環境,生產環境會分開java
Spring並非在構建的時候作出這樣的決策的,而是等到運行時再來肯定web
使用@Porfile
註解指定某個bean屬於哪個profile,算法
@Profile("dev")
表示profile環境是dev.spring
只有相應的profile激活的時候,纔會建立對應的bean,可是,沒有指定profile的bean始終都會被建立,與激活哪一個profile沒有關係數據庫
<beans xmlns="http://springframework.org/schema/beans" xmlns:... profile="dev"> </beans>
建立不一樣的XML文件設置不一樣的profile,也能夠在根<bean>
元素中嵌套定義<beans>
元素,而不是爲每個環境建立一個profile XML文件.app
Spring在肯定哪一個profile處於激活狀態時,須要依賴兩個獨立的屬性:spring.profiles.active和spring.profiles.default,若是設置了 spring.profiles.active 屬性,那麼它的值就會用來肯定哪一個profile是激活的,可是若是沒有設置,那 Spring會查找spring.profiles.defalut的值.若是二者均爲設置,那麼就沒有激活的profile,Spring只會建立哪些沒有定義在profile中的bean.oop
示例:
在web.xml中爲上下文設置默認的profile測試
<context-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </context-param>
在web.xml中爲Servlet設置默認的profileui
<servlet> <servlet-name>appServlet</servlet-name> <servlet-class> prg.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </init-param> </servlet>
Spring提供了@ActiveProfiles
註解指定運行測試時要激活哪一個profile.this
Spring4之後,引入了@Conditional
,能夠用到帶有@Bean
註解的方法上,用於計算給定的條件若是爲true,就會建立這個bean,不然的話這個bean會被忽略
例若有一個Test的類,咱們但願只有設置了 test 環境屬性的時候,Spring纔會實例化這個類,若是沒有,就會被忽略.下面的配置展現了註解的使用方式
@Bean @Conditional(TestExistsCondition.class) public Test test() { return new Test(); }
@Condition
中給定了一個Class,指明建立該bean的條件,設置給它的類能夠是任意實現了Condition接口的類型.下面展示了TestExistsCondition類的實現
public class TestExistsCondition() { public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment env = context.getEnvironment(); return env.containsProperty("test"); } }
其中的兩個參數 ConditionContext, AnnotatedTypeMetadata 都是接口,具體請看P77
在使用自動裝配時,若是一個接口有多個實現類而且都被定義爲bean,當Spring試圖裝配的時候就會沒法作出選擇從而拋出 NoUniqueBeanDefinitionException, Spring提供了兩種方式來解決這樣的問題:
在 bean 上使用@Primary註解標示當前的bean是首選bean,若是你是用XML來實例化bean
,能夠用下面的方式:
<bean id="iceCream" class="com.IceCream" primary="true"> </bean>
可是,若是你設置了兩個或者更多的首選bean,那麼它就沒法正常工做了
@Qualifier註解是使用限定符的主要方式:
@Autowired @Qualifier("iceCream") public void setDessert(Dessert dessert) { this.dessert = dessert }
上面的代碼片斷是最簡單的例子,爲@Qualifier註解所設置的參數就是想要注入的bean的ID,全部的@Component註解建立的類的ID都是首字母變爲小寫的類名.須要注意的是,若是沒有指定其餘的限定符的話,全部的bean都會給定一個默認的限定符,這個限定符與bean的ID相同
這裏有個問題就是,指定的限定符與要注入的bean的名稱是緊耦合的,若是bean的名字修改了,那麼就會沒法注入,解決方案是在bean上使用@qualifier
指定該bean的限定符
若是iceCream同時被使用在兩個或更多的bean上,那怎麼辦呢?你可能會想到的是使用更多的限定符來縮小範圍:
@Component @Qualifier("iceCream") @Qualifier("cold") public class IceCream implements Dessert {...}
這裏只有一個小問題,Java不容許在同一個條目上重複出現相同類型的多個註解.若是你這麼作了,編譯將會報錯.可是,咱們能夠建立自定義的限定符註解:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Qualifier public @interface Cold{}
上面的代碼片斷定義了一個自定義註解,它自己要使用@Qualifier來標註,這樣就能夠不斷縮小範圍,直到惟一.
在默認狀況下,Spring應用上下文中全部的bean都是做爲以單利的形式建立的,不過,Spring提供了多種做用域,能夠基於這些做用域建立bean:
默認是單例模式,若是選擇其餘的做用域,要使用@Scope註解,例如,要將一個bean生命爲原型,須要這樣作:
@bean @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Notepad{...}
使用組件掃描來發現和聲明bean,只須要加上@Component
註解替換@Bean
,一樣,若是使用XML來配置bean的話,可使用scope屬性來設置做用域:
<bean id="notepad" class ="com.notepad" scope="prototype" />
對於會話做用域,在當前會話的相關操做中,bean其實是單例的:
@Component @Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES) public ShoppingCart cart() {...}
要注意的是,同時還有一個proxyMode屬性被設置,請看下面的代碼:
@Component public class StoreService { @Autowired public void setShoppingCart(ShoppingCart shoopingCart) { this.shoppingCart = shoppingCart } }
這個service是一個單例的bean,當它建立的時候,Spring會試圖將ShoppingCart bean注入,可是ShoppingCart是會話做用域的,此時並不存在,知道某個用戶進入到系統纔會出現實例,另外,系統中將會有多個ShoppingCart實例被建立,咱們但願注入的bean恰好是當前會話對應的那一個,所以,Spring會向StroeService中注入一個到ShoppingCart的代理.關於具體的描述,請看P87
在代碼中,最好的方式是不要在代碼中出現硬代碼,所以咱們須要藉助Spring的一些配置來注入外部的值:
@Configuration @PropertySource("") public class ExpressiveConfig { @Autowired Environment env; @Bean public BlankDisc disc() { return new BlankDisc(env.getProperty("disc.title")); } }
咱們使用了@ PropertySource
註解引用了外部的一個屬性文件,其中有disc.title屬性,會被讀取並配置到bean中,若是這個屬性沒有被定義,獲取到的值會是null,若是你但願這個屬性必須被定義,可使用getRequiredProperty()方法.
另外,可使用Environment.containsProperty()方法來判斷是否有某一個屬性.最後,若是想將屬性解析爲類的話,可使用getPropertyAdClass()方法.
若是咱們依賴組件掃描和自動裝配來建立和初始化應用組件的話,娜美可使用@Value
註解,它與@Autowired
很是類似:
public BlankDisc(@Value("${disc.title}") String title) { this.title = title; }
爲了使用佔位符,咱們必需要配置一個PropertyPlaceHolderConfigurer bean或 PropertySourcePlaceHolderConfigurer bean,具體請看P92
Spring 3.2之後,註解支持在類和方法級別上使用
@Profile("...")
括號中能夠定義任意的字符串,但建議用有意義的字符串