之前,我們?cè)O(shè)置Bean的屬性值時(shí),采用的都是硬編碼的形式。比如,在定義BlankDisc時(shí):
@Bean
public BlankDisc blankDisc(){
return new BlankDisc("This is BlankDisc", "The Beatles");
}
與之類似,采用XML的方式也是硬編碼:
<bean id = "blankDisc" class = "com.inject.service.BlankDisc"
c:_title = "This id BlankDisc"
c:_artist = "This Beatles" />
硬編碼的壞處想必大家都知道,所以我們就想能不能讓這些值在程序運(yùn)行時(shí)再確定。其實(shí)Spring已經(jīng)提供了兩種方式實(shí)現(xiàn)運(yùn)行時(shí)注入的功能:
- 屬性占位符(Property planceholder)
- Spring 表達(dá)式語(yǔ)言(SpEL)
1.使用屬性占位符注入外部的值
@Autowired
Environment env;
public BlankDisc blanKDiscBean2(){
return new BlankDisc(
env.getProperty("disc.title"),
env.getProperty("disc.artist"));
}
1.1深入學(xué)習(xí)Spring的Environment
Environment的getProperty()方法有四個(gè)重載方法:
- String getProperty(String key)
- String getProperty(String key,String defaultValue)
- T getProperty(String key, Class<T> type)
- T getProperty(String key, Class<T> type,T defaultValue)
前兩種形式的getProperty()方法都是返回String類型的值。后兩種重載方法不是直接返回字符串,而是將獲取到的值轉(zhuǎn)化為指定類型的值,比如獲取連接池中維持連接的總數(shù)量:
int connectionCount = env.getProperty("db.connoctionCount",Integer.class,30);
在使用getProperty()獲取屬性時(shí),如果這個(gè)屬性沒(méi)有定義,那他獲取的是null,如果我們不想讓這樣的情況發(fā)生,可以使用getRequiredProperty()方法:
//必須定義參數(shù)
env.getRequiredProperty("disc.title"),
env.getRequiredProperty("disc.artist"));
如果disc.title或disc.arist沒(méi)有定義的話,將會(huì)拋出IllegalStateException異常。
如果你想檢查某個(gè)屬性是否存在,那么可以調(diào)用containsProperty()方法:
boolean titleExists = env.containsProperty(“disc.title”);
如果你想將屬性解析為類的話,可以使用getPropertyAsClass()方法:
Class<BlankDisc> blankDisc = env.getPropertyAsClass("disc.class",BlankDisc.class);
除了屬性相關(guān)的功能外,Environment還提供了一些方法來(lái)檢查哪些profile處于激活狀態(tài):
- String [] getActiveProfiles():返回激活profile名稱的數(shù)組;
- String [] getDefaultProfile():返回默認(rèn)profile名稱的數(shù)組;
- boolean acceptsPrifiles(String ... profiles):如果environment支持給定的profile返回true。
1.2解析屬性占位符
在Spring裝配中,占位符的形式為“${...}”包裝的屬性名稱。下面我們使用屬性占位符在XML中解析tomcat連接池bean的屬性:
<!-- 數(shù)據(jù)源配置, 使用Tomcat JDBC連接池 -->
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<!-- Connection Info -->
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- Connection Pooling Info -->
<property name="maxActive" value="${jdbc.pool.maxActive}"/>
<property name="maxIdle" value="${jdbc.pool.maxIdle}"/>
</bean>
要使用占位符,需要配置PropertySourcesPlaceholderConfigurer bean:
@Bean
public static PropertySourcesPlaceholderConfigurer placehoderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
在Xml配置要使用到命名空間中的<context:property-placeholder>元素,它可以為我們生成PropertySourcesPlaceholderConfigurer bean:
<context:property-placeholder ignore-unresolvable="true"
location="classpath*:/application.properties,
classpath*:/application.local.properties" file-encoding="UTF-8"/>
2.使用Spring表達(dá)式語(yǔ)言進(jìn)行配置
Spring 3引入了Spring表達(dá)式(Spring Excpression Language,SpEL),他能夠以一種強(qiáng)大和簡(jiǎn)潔的方式將值裝配到bean屬性和構(gòu)造器參數(shù)中,這個(gè)過(guò)程中所使用的表達(dá)式會(huì)在運(yùn)行時(shí)計(jì)算得到值。SpEL的特性有以下幾點(diǎn):
- 使用bean的ID來(lái)引用bean
- 調(diào)用方法和訪問(wèn)對(duì)象的屬性
- 對(duì)值進(jìn)行算術(shù)、關(guān)系和邏輯運(yùn)算
- 正則表達(dá)式匹配
- 集合操作
2.1SpEl表達(dá)式樣例
格式 | 說(shuō)明 |
---|---|
#{1} | 得到的值就是1 |
#{T(System).currentTimeMillis() | 獲取當(dāng)前時(shí)間的毫秒值,T()表達(dá)式會(huì)將java.lang.System視為java中對(duì)應(yīng)的類型,通過(guò).方法調(diào)用對(duì)應(yīng)的靜態(tài)方法 |
#{blankDisc.title} | 獲取ID為blankDisc bean的屬性值 |
#{systemPropertys['disc.title']} | 通過(guò)systemPropertys對(duì)象引用系統(tǒng)屬性 |
上面幾個(gè)只是幾個(gè)基本的SpEl樣例,后面我們會(huì)著重介紹,下面我們先看看使用SpEL表達(dá)式裝配bean的屬性:
public BlankDisc(
@Value("#{systemPropertys['disc.title']}") String title,
@Value("#{systemPropertys['disc.artist']}") String artist){
this.title = title;
this.artist = artist;
}
上例中使用到@Value注解,在注解中設(shè)置SpEL表達(dá)式。上面我們學(xué)習(xí)了幾個(gè)簡(jiǎn)單的樣例,也學(xué)習(xí)了如何將SpEL表達(dá)式解析得到的值注入到bean中,那么我們來(lái)繼續(xù)學(xué)習(xí)一下SpEl表達(dá)式支持的基礎(chǔ)表達(dá)式吧。
2.2表示字面值
使用SpEL字面量樣式可以表示整數(shù)、浮點(diǎn)數(shù)、String以及Boolean類型的值:
#{1}
#{3.14159}
#{9.87E4}
#{'Hello'}
#{false}
2.3引入bean屬性和方法
SpEL能夠通過(guò)ID引入其他的bean、bean的屬性和bean的方法:
#{blankDisc}
#{blankDisc.title}
#{blankDisc.sayHello()}
如果還可以對(duì)方法的返回值做處理,直接調(diào)用返回值類型的屬性或方法,假設(shè)sayHello()返回值是String類型:#{blankDisc.sayHello().toUpperCase()}
,這樣就可以獲取返回值的大寫(xiě)字母形式。
但如果返回值是null,調(diào)用它的方法就會(huì)報(bào)空指針異常,為了預(yù)防這種情況,可以使用 ? 判斷獲得返回值是否為null:#{blankDisc.sayHello()?.toUpperCase()}
如果sayHello()方法返回值是null,SpEl將不會(huì)調(diào)用toUpperCase()方法,SpEL的返回值是null。
2.4在表達(dá)式中使用類型
如果SpEL表達(dá)式訪問(wèn)類作用域的方法或常量時(shí),要使用到T()這個(gè)關(guān)鍵的運(yùn)算符:#{T(java.lang.Math)}
運(yùn)算符的結(jié)果是一個(gè)Class對(duì)象,它代表了java.lang.Math。通過(guò)它我們可以訪問(wèn)Math的靜態(tài)方法和常量。
#{T(java.lang.Math).PI}
#{T(java.lang.Math).random()}
2.5SpEL運(yùn)算符
SpEL提供了多個(gè)運(yùn)算符,這些運(yùn)算符可以使用到SpEL表達(dá)式上:
運(yùn)算符類型 | 運(yùn)算符 | |
---|---|---|
算術(shù)運(yùn)算符 | +、- 、*、/ 、%、^ | |
比較運(yùn)算符 | <、>、 = 、<=、 >=、 lt、 gt、 eq、 ge | |
條件運(yùn)算符 | ?:(ternary)、 ?:(Elvis) | |
邏輯運(yùn)算符 | and、 or、not、 | |
正則表達(dá)式 | matches |
2.6計(jì)算正則表達(dá)式
SpEL通過(guò)matches運(yùn)算符支持表達(dá)式中的模式匹配,匹配成功返回true,否則否則返回false,下面我們判斷郵箱是否符合正則表達(dá)式:
#{admin.email matches '[a-zA-Z0-9.-%+-]+@[a-zA-Z0-9.-]+\\.com'}
2.7計(jì)算集合
使用SpEL引用列表中的一個(gè)元素:#{jubox.songs[4].title}
,獲取列表的第5個(gè)元素(基于0開(kāi)始),[] 是從集合或數(shù)組中按照索引取元素。
SpEL提供了查詢運(yùn)算符(.?[]),他會(huì)用來(lái)對(duì)集合進(jìn)行過(guò)濾,得到集合的一個(gè)子集。下面我們將Aerosmith的歌曲過(guò)濾出來(lái):
#{jukebox.songs.?[artist eq 'Aerosmith']}
.^[]查詢第一個(gè)匹配項(xiàng)
.$[]查詢最后一個(gè)匹配項(xiàng)
投影運(yùn)算符(.![]),將集合對(duì)象特定的字段投影到另一個(gè)集合中,下面我們將歌曲的名稱投影到另一個(gè)String類型的集合中:
#{jukebox.songs.![title]}
我們還可以過(guò)濾要投影的歌曲,下面我們獲取Aerosmith歌曲的title:
#{jukebox.songs.?[artist eq 'Aerosmith'.![title]]}
自此運(yùn)行時(shí)注入的全部?jī)?nèi)容已經(jīng)全部介紹完啦。終于完了啦,下個(gè)月我們開(kāi)始學(xué)習(xí)Spring的Aop機(jī)制,很期待吧,come on!