慢慢來比較快,虛心學技術
當我們討論依賴注入的時候,多數注意的都是將一個bena作爲屬性或構造器參數注入到另一個bean中。但是有時候我們也需要關注如何將值注入到方法參數或者屬性中去,在前面的文章中,我們所瞭解的注入都是在系統初始化的時候就已經寫死的值,可是有時候我們也需要在運行時才確定需要注入的值,Spring提供了兩種運行時注入的方式:
- 屬性佔位符( Property placeholder )。
- Spring 表達式語言( SpEL )
一、屬性佔位符
在Spring中,最簡單直接的外部值注入方式是屬性佔位符,通過讀取外部屬性文件的值並注入到bean中。此處涉及@PropertySource註解和之前提到的Enviroment對象
- @PropertySource:將目標路徑的屬性文件引入當前環境
- Enviroment對象:封裝了當前環境的屬性,用於讀取環境中的屬性
如,我們現在創建一個屬性文件application.properties和一個基本類BeseBean,一個JavaConfig配置類,並將application.properties對應的屬性值傳入BaseBean中
#application.properties
properties.name=Properties name
//BaseBean.class
public class BaseBean {
private String name="BaseBean";
}
//BaseConfig.class
@Configuration
@PropertySource("classpath:application.properties")
public class BeanConfig {
@Autowired
Environment environment;
//定義bean並將環境中key爲properties.name注入到BaseBean的構造函數參數中
@Bean
public BaseBean baseBean(){
return new BaseBean(environment.getProperty("properties.name"));
}
}
//測試類:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class,classes = {BeanConfig.class})
public class AppBaseConfigTest {
@Autowired
private BaseBean baseBean;
@Test
public void getBean(){
System.out.println(baseBean.getName());
}
}
//測試結果:並不是默認的“BaseBean”,而是properties文件中定義好的Properties name
Properties name
上述代碼演示了通過@PropertySource註解和Enviroment對象實現外部值注入到bean。實際上,Enviroment提供了幾個獲取環境屬性值的實現方法:
- String getProperty(String key); //以key獲取對應值(String類型)
- String getProperty(String key, String defaultValue); //以key獲取對應值,當不存在對應的key或對應value爲null時,使用defaultValue(String類型)
- <T> T getProperty(String key, Class<T> type); //以key獲取對應值(並強轉爲type類型)
- <T> T getProperty(String key, Class<T> type, T defaultValue); //以key獲取對應值(並強轉爲type類型),當不存在對應的key或對應value爲null時,使用defaultValue(type類型)
如:假設BaseBean內有屬性age類型爲Integer,price屬性類型爲double,系統注入如下:
#application.properties
properties.name=Properties name
properties.age=50
properties.price=1.5
//定義bean並將環境中key爲properties.name注入到BaseBean的構造函數參數中
@Bean
public BaseBean baseBean(){
BaseBean baseBean = new BaseBean(environment.getProperty("properties.name"));
baseBean.setAge(enviroment.getProperty("properties.age",Integer.class,new Integer(10)));
baseBean.setPrice(enviroment.getProperty("properties.price",Double.class,new Double(10.5)))
}
二、Spring表達式(SpEL)
SpEL( Spring Expression Language,Spring表達式語言),是一種靈活的表達式,它能夠以一種強大和簡潔的方式將值裝配到 bean 屬性和構造器參數中,在這個過程中所使用的表達式會在運行時計算得到值。
SpEL具備如下特性:
使用 bean 的 ID 來引用 bean ;
調用方法和訪問對象的屬性;
對值進行算術、關係和邏輯運算;
正則表達式匹配;
集合操作
利用這些特性可以爲我們執行很多強大的功能, SpEL 表達式要放到 “#{ ... }” 之中,使用在@Value()註解裏面
Ⅰ、使用字面量值
SpEL可以注入字面量值,使用方式:#{1}、#{'Test'},#{1.09},#{flase}等
如上述例子中的BaseBean,我們通過SqEL爲其注入一個name,注意:一定要去除默認構造函數,否則自動掃描不會執行目標構造函數
public class BaseBean {
private String name="BaseBean";
public BaseBean(@Value("#{'SqEL Name'}")String name){
this.name = name;
}
}
Ⅱ、引用bean屬性和方法
SpEL還可以根據bean的ID獲取到bean並將該bean的,該bean的屬性或執行該bean方法的返回值注入到參數中
如,將baseBean中的name屬性傳入sonBean的name屬性中
@Component
public class SonBean {
private String sonName="sonName";
public SonBean(@Value("#{baseBean.name}") String sonName){
this.sonName = sonName;
}
}
執行baseBean中的getName方法將返回值注入到SonBean的構造方法中:
@Component
public class SonBean {
private String sonName="sonName";
public SonBean(@Value("#{baseBean.getName())}") String sonName){
this.sonName = sonName;
}
}
假設SonBean中有BaseBean屬性,將baseBean示例注入到SonBean的構造方法中:
@Component
public class SonBean {
private String sonName="sonName";
private BaseBean baseBean=null;
public SonBean(@Value("#{baseBean}") BaseBean baseBean){
this.baseBean = baseBean;
}
}
注:如果一個類中存在多個構造函數,那麼使用自動化配置方式裝配Bean的時候會報裝配失敗異常
Ⅲ、在表達式中使用類型
有時候我們需要在SpEL中使用一些類作用域的方法和屬性,需要使用T()關鍵子運算符,其運算結果是一個Class,關鍵作用在於我們能夠利用它訪問靜態方法和屬性。
如:獲取Math類中的PI和隨機數方法
//獲取PI值
#{T(java.lang.Math).PI}
//獲取隨機數
#{T(java.lang.Math).random()}
Ⅳ、運算符
在SpEL中我們可以像正常java語法中一樣使用運算符,其中比較特殊也比較常用的是三目表達式 ?:
//判斷如果baseBean的age屬性值大於50,則返回該age,否則返回60
#{baseBean.age>50?baseBean.age:60}
//判斷baseBean的age是否null,如果是,則返回60
#{baseBean.age?:60}
Ⅴ、正則表達式(matches)
使用SpEL進行正則匹配依賴於matches運算符,該運算符返回一個boolean值,語法類似java
//baseBean的name是否全英文
#{baseBean.name matches '[a-zA-Z]*'}
Ⅵ、集合計算
使用SpEL可以對集合做很多方便的操作,主要依賴於"[]"運算符實現,如:
假設現有一個BaseBean列表
①獲取集合某個元素的name屬性:#{beanList[4].name}
②獲取字符串中的某個下標的字符:#{'The Test'[3]}
③獲取集合中符合條件的元素(查詢運算符:.?[]):#{beanList.?[name eq 'Mr D']}(查找name等於”Mr D“的bean)
④獲取集合中共符合條件的第一項元素(查詢運算符:.[]):#{beanList.[name eq 'Mr D']}(查找name等於”Mr D“的第一個bean)
⑤獲取集合中共符合條件的最後一項元素(查詢運算符:.[name eq 'Mr D']}(查找name等於”Mr D“的最後一個bean)
⑥將集合中的某個屬性投影到另一個集合中(投影運算符:.![]):#{beanList.![name]}(將beanList中元素的name單獨作爲一個列表返回)