《Spring實戰》-第三章:Bean的高級裝配(5)-運行時值注入

慢慢來比較快,虛心學技術

當我們討論依賴注入的時候,多數注意的都是將一個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)

⑤獲取集合中共符合條件的最後一項元素(查詢運算符:.[]):#{beanList.[name eq 'Mr D']}(查找name等於”Mr D“的最後一個bean)

⑥將集合中的某個屬性投影到另一個集合中(投影運算符:.![]):#{beanList.![name]}(將beanList中元素的name單獨作爲一個列表返回)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章