Spring入門(九):運行時值注入

Spring提供了2種方式在運行時注入值:java

  1. 屬性佔位符(Property placeholder)
  2. Spring表達式語言(SpEL)

1. 屬性佔位符

1.1 注入外部的值

1.1.1 使用Environment

通常狀況下,咱們會將一些值放到配置文件中,等程序運行時再把值注入到一些字段上。git

假如,咱們有一個test.properties配置文件,內容以下:github

book.author=wangyunfei
book.name=spring boot
author.age=30
複製代碼

如今咱們但願在程序運行時,把這個值分別賦值給字段bookAuthor和bookName,那麼該如何實現呢?spring

首先,新建配置類ExpressiveConfig以下:express

package chapter03.el;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;

@Configuration
@ComponentScan
@PropertySource("classpath:chapter03/el/test.properties")
public class ExpressiveConfig {
    @Autowired
    private Environment environment;

    public void outputResource() {
        System.out.println("book.name:" + environment.getProperty("book.name"));
        System.out.println("book.author:" + environment.getProperty("book.author"));
    }
}
複製代碼

這裏咱們使用@PropertySource註解引用了test.properties配置文件,這個文件的位置位於chapter03.el包下。微信

這個屬性文件會加載到Spring的Environment中,而後咱們就能夠調用getProperty()方法獲取到屬性值。dom

新建Main類,在其main()方法中添加以下測試代碼:maven

package chapter03.el;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExpressiveConfig.class);

        ExpressiveConfig expressiveConfig = context.getBean(ExpressiveConfig.class);

        expressiveConfig.outputResource();

        context.close();
    }
}
複製代碼

運行代碼,發現拋出java.io.FileNotFoundException異常,以下所示:測試

從報錯信息能夠看出, 這是提示找不到chapter03/el/test.properties這個文件,這是爲何呢?ui

帶着這個疑問,咱們看下target目錄下編譯後的代碼,以下所示:

從圖中能夠看出,咱們新建的test.properties和test.txt文件並無被編譯到target目錄下,因此纔會拋出異常。

這是由於,咱們新建文件的位置放在chapter03.el包下,而IDEA默認是不會把這些文件自動複製到target目錄下的,但咱們能夠在pom.xml中添加以下配置來解決該問題:

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.txt</include>
                <include>**/*.properties</include>
            </includes>
        </resource>
    </resources>
</build>
複製代碼

這裏咱們指定了txt和properties文件,若是須要,能夠繼續添加<include>標籤指定xml等文件。

再次運行測試代碼,輸出日誌以下所示:

book.name:spring boot

book.author:wangyunfei

此時target目錄下已經包含了咱們新建的2個文件:

若是指定的屬性值不存在,getProperty()會返回null,以下所示:

String workCity = environment.getProperty("author.workcity");
System.out.println("author.workcity:" + workCity);
複製代碼

輸出結果:

author.workcity:null

getProperty()還提供了1個重載,當指定的屬性值不存在時,能夠指定默認值:

String workCity = environment.getProperty("author.workcity", "上海");
System.out.println("author.workcity:" + workCity);
複製代碼

輸出結果:

author.workcity:上海

若是但願屬性值必須存在,可使用getRequiredProperty()方法,當屬性值不存在時,會拋出java.lang.IllegalStateException異常:

String workCity = environment.getRequiredProperty("author.workcity");
System.out.println("author.workcity:" + workCity);
複製代碼

getProperty()還提供了1個重載,能夠指定返回值的類型,好比咱們想返回Integer類型:

Integer authorAge = environment.getProperty("author.age", Integer.class);
System.out.println("author.age:" + authorAge);
複製代碼

輸出結果:

author.age:30

getProperty()還提供了1個重載,當指定的屬性值不存在時,不只能夠指定默認值,還能夠指定返回值類型:

boolean isMan = environment.getProperty("author.isMan", Boolean.class, true);
System.out.println("author.isMan:" + isMan);
複製代碼

輸出結果:

author.isMan:true

1.1.2 使用屬性佔位符

除了使用Environment獲取外部的屬性值,咱們還可使用屬性佔位符來獲取。

在Spring裝配中,佔位符的形式爲使用「${......}」包裝的屬性名稱。

新建Book類以下:

package chapter03.el;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Book {
    @Value("${book.name}")
    private String bookName;

    @Value("${book.author}")
    private String bookAuthor;

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getBookAuthor() {
        return bookAuthor;
    }

    public void setBookAuthor(String bookAuthor) {
        this.bookAuthor = bookAuthor;
    }
}
複製代碼

能夠發現,咱們在字段上添加了@Value註解,參數傳的值就是屬性佔位符,用來獲取屬性文件中指定的屬性值。

而後,在ExpressiveConfig配置類中添加以下代碼:

@Autowired
private Book book;

public void outputResource() {
     System.out.println("book.name:" + book.getBookName());
     System.out.println("book.author:" + book.getBookAuthor());
}
複製代碼

輸出結果:

book.name:spring boot

book.author:wangyunfei

2. Spring表達式語言

Spring表達式語言(Spring Expression Language,SpEL)是一種很是靈活的表達式語言,可以以一種強大和簡潔的方式將值裝配到bean屬性或者構造器參數中,在這個過程當中所使用的的表達式會在運行時計算值。

SpEL表達式要放到「#{......}」之中,而以前講到的屬性佔位符是放到「${......}」之中。

接下來,咱們分場景來看下Spring表達式語言的使用方法。

2.1 引用系統屬性值

在ExpressiveConfig中添加以下代碼:

@Value("#{systemProperties['os.name']}")
private String osName;

public void outputResource() {
    System.out.println("os.name:" + osName);
}
複製代碼

輸出結果:

os.name:Windows 7

2.2 引用bean的屬性和方法

首先,新建一個類DemoService以下所示:

package chapter03.el;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class DemoService {
    @Value("DemoService類的another屬性")
    private String another;

    public String getAnother() {
        return another;
    }

    public void setAnother(String another) {
        this.another = another;
    }
}
複製代碼

而後,在ExpressiveConfig中添加以下代碼:

@Value("#{demoService.another}")
private String fromAnother;

public void outputResource() {
    System.out.println("demoService.another:" + fromAnother);
}
複製代碼

表達式中的demoService爲DemoService bean的ID,another是它的屬性。

輸出結果:

demoService.another:DemoService類的another屬性

表達式也能夠修改成調用bean的方法:

@Value("#{demoService.getAnother()}")
private String fromAnother;
複製代碼

輸出結果不變,只是從調用屬性變成了調用方法。

調用完方法,能夠對方法的返回值繼續調用其它方法,好比toUpperCase():

@Value("#{demoService.getAnother()?.toUpperCase()}")
private String fromAnother;
複製代碼

之因此使用"?."運算符,是爲了不當demoService.getAnother()返回null時,代碼出現NullPointerException。

此時的輸出結果爲:

demoService.another:DEMOSERVICE類的ANOTHER屬性

2.3 在表達式中使用類型

使用表達式生成1個隨機數:

@Value("#{T(java.lang.Math).random()}")
private double randomNumber;

public void outputResource() {
    System.out.println("randomNumber:" + randomNumber);
}
複製代碼

這裏咱們使用T()引用了java.lang.Math類,而後調用了它的靜態方法random()。

輸出結果:

randomNumber:0.6801944394506442

2.4 使用運算符

上面的例子中,生成隨機數後,咱們還可使用乘法運算符,以下所示:

@Value("#{T(java.lang.Math).random() * 100.0}")
private double randomNumber;
複製代碼

咱們也能夠在表達式中使用「+」運算符拼接字符串,以下所示:

@Value("#{book.getBookName() + ' write by ' + book.getBookAuthor()}")
private String bookDescr;

public void outputResource() {
    System.out.println("bookDescr:" + bookDescr);
}
複製代碼

其中book爲Book bean的ID,輸出結果以下所示:

bookDescr:spring boot write by wangyunfei

也能夠在表達式中使用三元運算符:

@Value("#{systemProperties['os.name'] == 'Windows 7'?'Windows':'Linux'}")
private String osType;

public void outputResource() {
    System.out.println("osType:" + osType);
}
複製代碼

由於個人電腦系統是Windows 7,因此輸出結果以下所示:

osType:Windows

SpEL還支持不少的運算符,這裏只是列舉了幾個經常使用的例子,有興趣的同窗能夠本身深刻研究下。

3. 源碼及參考

源碼地址:github.com/zwwhnly/spr…,歡迎下載。

Craig Walls 《Spring實戰(第4版)》

汪雲飛《Java EE開發的顛覆者:Spring Boot實戰》

IDEA maven項目src源代碼下的資源文件不自動複製到classes文件夾的解決方法

最後,歡迎關注個人微信公衆號:「申城異鄉人」,全部博客會同步更新。

相關文章
相關標籤/搜索