Day 10 - 讀取 Spring Boot 設定檔 - @Value

古古

2023/11/10


哈囉大家好,我是古古

在前面的文章中,我們已經對 Spring IoC 有了滿多的認識,那麼接著這篇文章,我們會延伸出去,介紹一下要如何透過 @Value,將 Spring Boot 設定檔中的值給讀取到 Bean 裡面

什麼是 Spring Boot 設定檔?

所謂的 Spring Boot 設定檔,指的是「放在 src/resource 這個資料夾底下的 applicaiton.properties 檔案」,而他的目的,就是去「存放 Spring Boot 程式的設定值」

Spring Boot 設定檔(application.properties)的寫法

大家如果點擊兩下打開 application.properties 檔案的話,可以看到右邊是空白一片,表示我們目前還沒有在這個 application.properties 檔案中,設定任何的設定值

如果想要在這個 application.properties 的檔案裡面,添加 Spring Boot 的設定值的話,需要遵循一定的寫法格式才可以

首先大家可以觀察一下,application.properties 這個檔案,他是一個「檔名為 application、並且副檔名為 .properties」的檔案,也因為如此,這個 application.properties 檔案,他就是使用 「properties 這個語法」 來撰寫的

properties 的語法格式

在 properties 的語法中,是使用 key=value 這樣子的格式來撰寫,並且「每一行」都是一組 key 和 value 的配對

像是下面這個例子,我們就在第 1 行寫上了 count=5,所以這一行程式就表示,我們定義了一個 key 的名字是 count,並且這個 count 的 value 值,就是 5

大家可以把這個 key=value 的寫法,想像成是 變數=值 的概念,所以前面的 key 其實就等同於是 Java 中的變數,而後面的 value,就是這個變數的值,就是這麼的簡單暴力!

因此當我們在 application.properties 檔案中,去寫上了一行 count=5 的程式,就表示我們定義了一個變數 count,然後他的值是 5 這樣,僅此而已

1. properties 語法的注意事項之一:不需要加上空白鍵排版

大概了解了 properties 語法的核心概念 key=value 之後,接著我們可以來看一些使用 properties 語法的注意事項

當我們以前在寫 Java 程式的時候,習慣會在 = 的前後加上一堆空白鍵,去做排版的美化,像是下面這個樣子:

// 美化前
int count=5;

// 加上空白鍵美化後
int count = 5;

不過在 properties 語法裡面,是 「不需要」= 的前後,去加上空白鍵去做排版的美化,只要全部連在一起寫就好,多加空白鍵反而可能有機會導致程式運行出現問題

所以在 properties 語法裡面,建議就是使用下面這種寫法,把你的空白鍵拔掉,一路連自連到底就對了

count=5

2. properties 語法的注意事項之二:key 中的 . 表示「的」的概念

在前面有提到,properties 語法中是使用 key=value 來撰寫程式的,而 key 所代表的,就是變數的名字

不過這個 key 在命名上,是允許裡面帶上 . 符號的,並且這個 . 的符號,他的邏輯意義就是中文的「的」的意思

舉例來說,我們可以在 application.properties 檔案裡面,在第 2 行寫上 my.name=John 的程式,而當我們這樣寫之後,my.name 這個 key 就是變數的名字,其中文意義是表示「我的名字」(因為 . 是表示「的」的意思)

所以 my.name=John 這一整行的意思,就是「我的名字叫做 John」

或是我們也可以在下面,再新增一行 my.age=20 的程式,而這一行程式所代表的,就是「我的年齡是 20 歲」的意思

所以大家以後就可以將 key 中的 .,去翻譯成是中文的「的」的意思,這樣就可以透過這種格式,去傳遞更豐富的意義出來

3. properties 語法的注意事項之三:使用 # 來表示 comment

在 properties 語法中,也是可以去添加 comment 的,只要在最前面加上一個 # 的符號,那一行就會被 properties 語法給忽略了(用法同 Java 中的 // 一樣)

讀取 Spring Boot 設定檔(application.properties)中的值:@Value

了解了要如何在 application.properties 檔案中透過 key=value 的寫法,去添加一行行的設定之後,接著我們可以來看一下,要如何透過 @Value,將 application.properties 中的設定值讀取到 Bean 裡面

還是之前的 HpPrinter 的例子,我們可以稍微改寫一下 HpPrinter 中的程式,讓他變成下面這個樣子

@Component
public class HpPrinter implements Printer {

    private int count;

    @Override
    public void print(String message) {
        count--;
        System.out.println("HP印表機: " + message);
        System.out.println("剩餘使用次數: " + count);
    }
}

而在這之中,count 變數的值沒有被設定成任何一個數字,因此 count 值就會是預設的 0

但是如果我們在這個 count 的變數上面,去添加一個 @Value("${count}") 的話,這樣子就可以從 Spring Boot 設定檔 (application.properties 檔案) 中,去讀取其中的 count 這個 key 的值,並且將這個值給賦予到 HpPrinter 中的 count 變數裡面

而因為目前在 application.properties 檔案中,我們是在第 1 行,去寫上了 count=5 的設定

因此到時候 HpPrinter 中的 count 變數的值,就會變成是 application.properties 檔案中所定義的「5」

所以這也就表示,我們就成功的透過 @Value 的用法,將 Spring Boot 設定檔 (application.properties 檔案) 中的值,給成功的讀取到 Bean 裡面的變數中了!

1. 使用 @Value 的注意事項之一:需要遵守固定格式寫法

大概了解了 @Value 的用法之後,我們可以來看一下使用 @Value 的一些注意事項

在使用 @Value 去讀取 Spring Boot 設定檔 (application.properties 檔案) 中的值的時候,一定要在 @Value 後面的括號中,寫上如下的格式:

@Value("${XXXX}")

其中 XXXX 可以替換成 application.properties 檔案中的任意一個 key,但是外層的 "${}" 是不能夠省略的,因此在寫法上會稍微複雜一點,建議大家之後可以直接複製貼上這段程式,再去改裡面的 XXX 的值即可

舉例來說,假設 application.properties 中的 key 為 printer.count

printer.count=100

那麼在使用 @Value 去注入時,就是把後面的 XXX 替換成 printer.count,所以就會變成是:

@Value("${printer.count}")
private int count;

因此就可以透過這樣的寫法,將 application.properties 中的設定值,給注入到 Bean 中的變數了

2. 使用 @Value 的注意事項之二:只有在 Bean 和 Configuration 中才能生效

只要是有使用到 @Value 的地方,該 class 本身得是一個 Bean、或是一個 Configuration 的設定 class@Value 才能夠生效

所以假設大家在使用 @Value 時,明明程式就寫得很對,但是不知道為什麼他不起作用,很大的原因就是因為這個 class 還沒變成 Bean,所以 @Value 才會毫無作用

因此發生這種情況時,只要記得在 class 上面加上一個 @Component,將這個 class 變成是一個 Bean,這樣子就可以確保 @Value 能生效了

補充:@Value 在那些帶有 @Configuration 的 class 中,也是能夠生效的,不過因為在此系列文中不會特別介紹到 @Configuration 的用法,所以大家可以先有個印象就可以了

3. 使用 @Value 的注意事項之三:類型需要一致

在使用 @Value 去讀取 Spring Boot 設定檔 (application.properties 檔案) 中的值時,Java 中的變數的類型,必須要和 application.properties 中的類型一致才可以

舉例來說,在 application.properties 檔案裡面,我們定義了一組 key 和 value 如下:

printer.count=5

上面這行程式,其實就是在暗示 printer.count 的值為「一個整數」,因此在 Spring Boot 程式中,我們就得將 @Value 加在一個「Int 或是 Long 類型」的變數上,這樣子在賦予值的時候才不會出現問題

@Value("${printer.count}")
private int count;

又或是說,假設我們在 application.properties 檔案裡面,定義了另一組 key 和 value 如下:

my.name=John

上面這行程式,也是在暗示 my.name 的值為「一個字串」,因此在 Spring Boot 程式中,我們就得將 @Value 加在一個「String 類型」的變數上,這樣子在賦予值的時候,也才不會出現問題

@Value("${my.name}")
private String name;

4. 使用 @Value 的注意事項之四:可以設定預設值

在使用 @Value 去讀取 Spring Boot 設定檔 (application.properties 檔案) 中的值時,有可能會發生一種情況,就是「該 key 不存在在 application.properties 檔案裡面」

譬如說我們在 Spring Boot 程式裡面,寫上了下面這一段程式,嘗試去將 application.properties 檔案中的 printer.count 的值,去儲存到 count 變數中

@Value("${printer.count}")
private int count;

如果這時候 application.properties 檔案中「沒有」printer.count 這個 key 的話,那麼 Spring Boot 程式就會運作失敗,出現「Could not resolve placeholder ‘printer.count’ in value “${printer.count}"」的錯誤,導致無法成功啟動

如果大家想要避免這個問題的話,@Value 也是有提供另一種輔助方式可以讓我們使用,即是「設定預設值」

我們可以在 @Value 的 key 的後面,加上一個 :,並且在後面寫上想要的值,譬如說這裡寫上 200

@Value("${printer.count:200}")
private int count;

上面這一段程式的意思是:「@Value 會先去找尋 application.properties 中有沒有 printer.count 這個 key,有的話,就去讀取那個 key 的值到 count 變數中,如果沒有的話,則將 count 變數的值,設定成預設的 200」

所以當我們這樣寫之後,如果 application.properties 中「有設定」printer.count 的值(如下方程式所示),那麼 Spring Boot 程式中的 count 變數的值就會是 5

printer.count=5

但是如果 application.properties 中,「沒有設定」printer.count 的值(如下方程式所示),那麼 Spring Boot 程式中的 count 變數的值就會是預設的 200

# no key-value

所以簡單的說的話,如果想要「避免 application.properties 中找不到該 key,導致 Spring Boot 程式運行失敗」的狀況的話,那麼就可以在 @Value 中使用 :,在後面加上預設值,當找不到 key 時,就可以直接無縫接軌改成使用預設值來運行

補充:使用 @Value 的預設值寫法,其實是有點雙面刃的做法,因為這樣會讓設定值四散在各處,後續會比較難進行管理,因此建議大家斟酌使用

小結:所以,@Value 到底要怎麼用?

所以說到這邊,我們可以來總結一下 @Value 的用法和注意事項

想要使用 @Value 去讀取 Spring Boot 設定檔 (application.properties 檔案) 中的值的話,必須滿足:

  1. 必須使用固定格式 @Value("${XXXX}")
  2. 該 class 必須是 Bean 或是 Configuration
  3. Java 中的變數和 application.properties 中的類型需要一致
  4. 可以視情況添加預設值

只要注意好這些地方,大家以後就可以透過 @Value,在 Spring Boot 程式中去讀取 Spring Boot 設定檔 (application.properties 檔案) 中的值了!

補充 1:Spring Boot 設定檔的兩種語法(properties 和 yml)

在前面我們有介紹到,Spring Boot 的設定檔所指的,就是「application.properties」這個檔案,而他裡面所放的,就是 Spring Boot 程式的設定值

不過其實 Spring Boot 設定檔,是可以使用兩種不同的語法來撰寫的,分別是 properties 和 yml

  • 當 Spring Boot 設定檔 「命名成 application.properties」,即是表示他是使用 properties 語法來撰寫,因此格式是 key=value
  • 當 Spring Boot 設定檔 「命名成 application.yml」,即是表示他是使用 yml 語法來撰寫,格式則為 key: value

所以在 Spring Boot 程式裡面,application.properties 和 application.yml 這兩個檔案,他們的目的都是一樣的,都是去作為 Spring Boot 的設定檔,去儲存相關的設定值,差別只在他們使用了不同的語法來撰寫而已

不過在這裡要特特特特別注意的一點,就是 「一份 Spring Boot 程式中只能使用一種語法來撰寫」,換句話說的話,就是 application.properties 和 application.yml 這兩個檔案,他們「不能同時存在」

所以一開始在創建 Spring Boot 程式時,要不就是選用 application.properties 寫到底,要不就是使用 appilcation.yml 寫到底,不能夠讓這兩個檔案同時並存,否則會使得 Spring Boot 程式運作失敗,這是大家在使用上要特別注意的地方

補充:我自己是比較喜歡使用 application.properties 來設定,yml 語法寫起來感覺很容易出錯😂(一個恍神不小心縮排就毀了),供大家參考

補充 2:yml 的語法

在前面我們有詳細介紹了 properties 語法的用法,而這裡也可以來補充一些 yml 語法的寫法

key: value 的寫法

在 yml 語法中,是採用 key: value 的方式來撰寫,注意在 value 前面 「必須」 加上一個空白鍵來隔開 :,所以寫起來的效果如下:

count: 5

上面這一段程式所表示的,就是去宣告一個 key 為 count,並且他的值為 5,而如果想用 properties 語法來表示同樣的效果的話,則會寫成下面這樣子:

count=5

縮排的寫法

yml 除了使用 key: value 的方式來設定值之外,並且 yml 是使用「縮排」的方式,來表示中文的「的」的概念

像是在 properties 語法中,當我們寫了下面這行程式,其中文意義是表示「我的名字叫做 John」(因為 properties 語法中的 .,就是表示中文的「的」的意思)

my.name=John

而在 yml 中,則會寫成下面這個樣子,透過 「將 name 這一行往右縮排 2 個空白鍵」,表示中文的「的」的意思

my:
  name: John

所以上面這一段程式讀起來,一樣也是「我的名字是 John」,只是 yml 是透過縮排的方式來表達,而 properties 是使用 . 的方式來表達而已

小結:properties 和 yml 的差別

下面總結了 properties 語法和 yml 語法在寫法上的差別,基本上是大同小異,就是「縮排」的這個概念需要轉換一下,其他部分(像是 key 和 value 的寫法),大致上都還是滿相似的

總結

這篇文章先介紹了什麼是 Spring Boot 的設定檔,也介紹了 application.properties 檔案的用途,以及 properties 的語法介紹

並且我們也介紹到,要如何使用 @Value,將 Spring Boot 設定檔 (application.properties 檔案) 中的值讀取到 Bean 裡面,也介紹了使用 @Value 的注意事項和預設值的用法

最後則是補充了 Spring Boot 設定檔的兩種命名方式,也介紹了 yml 的語法,以及 properties 語法和 yml 語法的差別

所以到這篇文章為止,有關於 Spring IoC 的介紹就告一個段落了,在這個章節中:

  • 我們先介紹了 Spring IoC 中最重要的兩個概念:IoC 和 DI
  • 也介紹了什麼是 Spring 容器以及 Bean,並且介紹要如何透過 @Component@Autowired@Qualifier 以及 @PostConstruct 這些註解,去對 Bean 進行創建、注入、以及初始化
  • 最後也介紹了要如何透過 @Value,將 Spring Boot 設定檔中的值讀取到 Bean 裡面

那麼從下一篇文章開始,我們就會來了解 Spring 框架中的另一個也很重要的特性:AOP,那我們就下一篇文章見啦!

相關連結