本節主要講:java
當須要根據環境決定該建立哪一個 bean 和不建立哪一個 bean,可使用bean profile 的功能,確保是等到運行時再來肯定,這樣的結果就是同一個部署單元(可能會是 WAR 文件)可以適用於全部的環境,沒有必要進行從新構建。正則表達式
在 Java 配置中,可使用 @Profile 註解指定某個 bean 屬於哪個 profile,例如,在配置類中,嵌入式數據庫的 DataSource 可能會配置成以下所示:spring
package com.myapp; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.jndi.JndiObjectFactoryBean; @Configuration public class DataSourceConfig { @Bean(destroyMethod = "shutdown") @Profile("dev") public DataSource embeddedDataSource() { return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.H2) .addScript("classpath:schema.sql") .addScript("classpath:test-data.sql") .build(); } @Bean @Profile("prod") public DataSource jndiDataSource() { JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setJndiName("jdbc/myDS"); jndiObjectFactoryBean.setResourceRef(true); jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class); return (DataSource) jndiObjectFactoryBean.getObject(); } }
有多種方式來設置這兩個屬性:sql
僅有一個 bean 匹配所需的結果時,自動裝配纔是有效的。若是不只有一個 bean 可以匹配結果的話,這種歧義性會阻礙 Spring 自動裝配屬性、構造器參數或方法參數。數據庫
採用@Primary只能標記一個首選bean,若是你標示了兩個或更多的首選 bean,那麼它就沒法正常工做了。app
package com.food; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @Component @Primary public class Cake implements Dessert { }
設置首選 bean 的侷限性在於 @Primary 沒法將可選方案的範圍限定到惟一一個無歧義性的選項中。它只能標示一個優先的可選方案。當首選 bean 的數量超過一個時,咱們並無其餘的方法進一步縮小可選範圍。dom
咱們這裏使用自定義的限定符註解。當使用自定義的 @Qualifier 值時,最佳實踐是爲 bean 選擇特徵性或描述性的術語,而不是使用隨意的名字。測試
建立自定義的限定符註解,這裏要使用@Qualifier來標註一下:ui
package com.food; import javax.inject.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface Cold { }
爲組件添加@Cold註解:this
package com.food; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component @Cold public class IceCream implements Dessert { }
爲了獲得 IceCream bean,DiningTable() 方法能夠這樣使用註解:
package com.food; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class DiningTable { private Dessert dessert; @Autowired @Creamy public DiningTable(Dessert dessert) { this.dessert = dessert; } }
當討論依賴注入的時候,咱們一般所討論的是將一個 bean 引用注入到另外一個 bean 的屬性或構造器參數中,可是 bean 裝配的另一個方面指的是將一個值注入到 bean 的屬性或者構造器參數中。
例如:
@Bean public CompactDisc sgtPeppers() { return new BlankDisc( "Sgt. Pepper's Lonely Hearts Club Band", "The Beatles" ); }
BlankDisc bean 設置 title 和 artist,但它在實現的時候是將值硬編碼在配置類中的,有時候硬編碼是能夠的,但有的時候,咱們可能會但願避免硬編碼值,而是想讓這些值在運行時再肯定。
Spring提供了兩種在運行時求值的方式:
屬性佔位符須要放到 ${ ... }
之中。
在本例中,@PropertySource 引用了類路徑中一個名爲 app.properties 的文件。它大體會以下所示:
disc.title=Sgt. Peppers Lonely Hearts Club disc.artisc=The Beatles
若是咱們依賴於組件掃描和自動裝配來建立和初始化應用組件的話,那麼就沒有指定佔位符的配置文件或類了。好比,在 BlankDisc 類中,構造器能夠改爲以下所示:
public BlankDisc(@Value("${disc.title}") String title, @Value("${disc.artist}") String artist) { this.title = title; this.artist = artist; }
@Value的使用方式與 @Autowired 註解很是類似。
SpEL 擁有不少特性,包括:
須要瞭解的第一件事情就是 SpEL 表達式要放到 #{ ... }
之中。
若是經過組件掃描建立 bean 的話,在注入屬性和構造器參數時,咱們可使用 @Value 註解,例如:
public BlankDisc(@Value("#{systemProperties['disc.title']}") String title, @Value("#{systemProperties['disc.artist']}") String artist) { this.title = title; this.artist = artist; }
#{3.14159} #{false} #{'3.14159'} #{9.78E4}//科學計數法
#{sgtPeppers}//經過 ID 引用其餘的 bean #{sgtPeppers.artist}//引用sgtPeppers的artist屬性 #{sgtPeppers.selectArtist()}//引用sgtPeppers的方法 #{sgtPeppers.selectArtist()?.toUpperCase()}//?.這個運算符可以在訪問它右邊的內容以前,確保它所對應的元素不是 null。因此,若是 selectArtist() 的返回值是 null 的話,那麼 SpEL 將不會調用 toUpperCase() 方法。表達式的返回值會是 null。
T(java.lang.Math)//這裏所示的 T() 運算符的結果會是一個 Class 對象,表明了 java.lang.Math。
T()
運算符的真正價值在於它可以訪問目標類型的靜態方法和常量。
T(java.lang.Math).PI T(java.lang.Math).random()
matches 返回的是一個boolean類型的值
#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}
//引用一個jukebox bean的一個songs集合的title屬性 #{jukebox.songs[4].title} //.?[] 運算符用來對集合進行過濾,獲得集合的一個子集 //獲得jukebox 中 artist 屬性爲 Aerosmith 的全部歌曲 #{jukebox.songs.?[artist eq 'Aerosmith']} //.^[] 用在集合中查詢第一個匹配項 #{jukebox.songs.^[artist eq 'Aerosmith']} //.$[] 用在集合中查詢最後一個匹配項 #{jukebox.songs.$[artist eq 'Aerosmith']} //.![]從集合的每一個成員中選擇特定的屬性放到另一個集合中 #{jukebox.songs.![title]}