Day 10 - 讀取 Spring Boot 設定檔 - @Value、application.properties

古古

2024/07/10


哈囉大家好,我是古古。

在前面的文章中,我們已經對 Spring IoC 有了滿多的認識,並且也能夠在 Spring Boot 中應用 Spring IoC 的核心用法了。

那麼接著這篇文章,我們就會繼續深入來介紹,要如何透過 @Value,將 Spring Boot 設定檔中的值讀取到 Bean 裡面,讓我們所寫的 Java 程式可以去運用 Spring Boot 設定檔中的值,所以我們就開始吧!

什麼是 Spring Boot 設定檔? #

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

所以大家以後只要聽到別人在說「Spring Boot 設定檔」,就要第一時間想到他所指的其實是 application.properties 這個檔案。

如果我們點擊兩下打開 application.properties 檔案的話,可以看到右邊有一行 spring.application.name=demo 的程式,這個是 IntelliJ 在創建 Spring Boot 專案時所自動生成的設定值。

這裡大家可以自由選擇要不要將 spring.application.name=demo 這一行程式刪掉(刪掉此設定不影響 Spring Boot 運作),如果不想刪除的話,也可以直接按下 Enter 鍵換行,這樣子就可以繼續在這個 application.properties 檔案中,繼續添加其他的設定值了。

補充:由於版面的緣故,因此在下面的截圖中不會出現 spring.application.name=demo 的設定。

application.properties 的寫法 #

如果想要在 application.properties 中,添加 Spring Boot 的相關設定的話,那就要遵循一定的寫法格式才可以。

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

properties 的語法介紹:key=value #

而在 properties 的語法中,是使用 key=value 的格式來撰寫設定值,並且每一行程式,就是一組 key 和 value 的配對。

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

所以大家其實可以把這個 key=value 的寫法,簡單的想像成是 變數=值 的概念,在前面的 key 就等同於是 Java 中的變數名稱,而後面的 value,就是這個變數的值,所以 properties 語法的概念其實和 Java 是很相近的!

因此當我們在 application.properties 中,寫上了一行 count=5 的程式時,就表示我們定義了一個變數 count,然後他的值是 5 這樣,就是這麼的簡單暴力!

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

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

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

// 美化前
int count=5;

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

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

所以在 properties 語法裡面,建議就使用下面這種寫法,把你的空白鍵收起來,一路連字連到底就對了!

count=5

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

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

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

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

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

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

所以大家以後在撰寫 properties 語法時,就可以將 key 中的 .,翻譯成是中文的「的」的意思,所以我們就可以透過這種方式,去傳遞更豐富的邏輯意義出來了。

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

在 properties 語法中,我們也是可以去添加 comment 的,只要在撰寫程式時,在最前面加上一個 # 的符號,那麼那一行就會被 properties 語法給忽略了(用法和 Java 中的 // 一樣)。

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

@Value 用法介紹 #

在了解了 Spring Boot 設定檔(也就是 application.properties 檔案)的用途、以及 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);
    }
}

在這段程式之中,HpPrinter 中的 count 變數沒有被初始化,因此 count 的值預設就會是 0。

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

而又因為我們前面有在 application.properties 的檔案中,在第 1 行寫上了 count=5 的設定。

所以到時候 HpPrinter 中的 count 變數的值,就會被設定成是 application.properties 中所定義的「5」了!

@Value 的運行結果 #

當大家改寫好上述的程式之後,我們也可以試著來運行一下 Spring Boot 程式,來實際感受一下 @Value 的效果。

以目前的程式來說(記得將 MyController 的 @Qualifier 的值改回成 hpPrinter),只要大家運行起 Spring Boot 程式,並且請求 http://localhost:8080/test 之後,IntelliJ 就會在 console 下方呈現下列的資訊。

其中之所以會輸出「剩餘使用次數: 4」,就是因為我們有在 HpPrinter 中添加一行 @Value("${count}") 的程式,因此 HpPrinter 中的 count 的值就會被設定成 5,才導致這裡會輸出「剩餘使用次數為 4 次」的資訊。

大家如果想玩玩看的話,也可以試著把 application.properties 中的 count 的值改成別的數字,譬如說改成 count=10 之類的。修改完之後,只要重新運行 Spring Boot 程式,並且重新請求 http://localhost:8080/test,這樣子在 console 中所輸出的剩餘使用次數,就會變成是 99 了。

總而言之,透過上述的運行結果,就代表我們能夠成功的透過 @Value 的用法,將 application.properties 中的設定值讀取到 Bean 裡面了!

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

大概了解了 @Value 的用法之後,接著我們也來介紹一下使用 @Value 的一些注意事項。

在使用 @Value 去讀取 application.properties 中的值時,一定要使用下面這種格式撰寫 @Value 的程式才可以:

@Value("${XXX}")

在這個格式中,其中的 XXX 的部分,可以替換成 application.properties 中的任意一個 key,但是外層的 "${}" 是不能夠省略的!!切記!!!

也因為 @Value 的固定格式寫法確實比較複雜一點,因此就建議大家,可以直接複製上面這段程式,後續再去修改裡面的 XXX 的值即可。

舉例來說,假設我們想要讀取 application.properties 中的 printer.count 這個 key,那我們就可以這樣寫:

printer.count=100

而後續在使用 @Value 去讀取時,就直接把後面的 XXX 替換成 printer.count 就可以了,所以就可以在 Java 中實作下面這些程式:

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

因此透過上述的寫法,我們就可以使用 @Value,去讀取 application.properties 中的設定值到 Bean 裡面了!

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

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

所以當大家在使用 @Value 時,當你覺得你的程式明明寫得很對,但是不知道為什麼卻沒有作用時,這時候就可以回頭檢查,是不是因為這個 class 還沒變成 Bean,所以 @Value 才會毫無作用。

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

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

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

在使用 @Value 去讀取 application.properties 中的值時,那個被添加 @Value 的 Java 變數,他的變數類型必須要和 application.properties 中的類型一致才可以。

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

printer.count=5

上面這行程式的意思,是表示 printer.count 的值為 5,而 5 這個數字,就暗示了他的類型為「整數」。

因此在 Spring Boot 的程式中,我們就必須將 @Value 加在一個「int 或是 long 類型」的變數上,這樣子到時候 @Value 在讀取值時,才不會出現問題。

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

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

my.name=John

上面這行程式的意思,是表示 my.name 的值為 John,而 John 這個單字,就暗示了他的類型為「字串」。

因此在 Spring Boot 的程式中,我們就必須將 @Value 加在一個「String 類型」的變數上,這樣子到時候 @Value 在讀取值時,也才不會出現問題。

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

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

在使用 @Value 去讀取 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}"」的錯誤,表示找不到 printer.count 這個 key,所以才會導致運行失敗。

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

像是我們在撰寫 @Value 的程式時,可以在 @Value 中的 key 的後面加上一個 :,並且在後面寫上想要的預設值,譬如說這裡我們就寫上 200:

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

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

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

printer.count=5

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

# no key-value

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

補充:不過老實說,@Value 的預設值的用法其實是有好有壞,因為當我們使用了 @Value 的預設值之後,就會讓設定值四散在各個 calss 裡面,後續會比較難統一進行管理,因此建議大家斟酌使用。

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

因為 @Value 的注意事項比較多,所以我們也可以來總結一下 @Value 的用法和注意事項有哪些。

想要使用 @Value 去讀取 Spring Boot 設定檔(也就是 application.properties 檔案)中的值的話,必須注意以下的事項:

  • 必須使用固定格式 @Value("${XXXX}")
  • 該 class 必須是 Bean 或是 @Configuration
  • 「Java 中的變數」和「application.properties 中的 key」,他們的類型必須要一致
  • 可以視情況添加預設值

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

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

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

不過其實在 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 這兩個檔案,不能同時存在,只能擇一使用,這是大家在使用上要特別注意的地方。

補充:雖然我個人是比較喜歡使用 application.properties,不過實務上兩種語法都會看到,因此建議大家兩種語法都還是要熟悉一下會比較好。

補充二:yml 的語法介紹 #

在前面我們有詳細介紹了 properties 語法的用法,在這裡我們也來補充一些 yml 語法的寫法,提供給大家參考。

key:value 的寫法 #

不同於 properties 語法,在 yml 語法中,是採用 key: value 的方式來撰寫設定值。

不過這裡要特別注意,在 : 的後面,必須要先加上一個空白鍵,然後才能寫上 value 值,這是在撰寫 yml 語法的時候,要特別留意的地方。

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 的寫法上是很類似的,但就是「縮排」的概念需要轉換一下,這是大家在實作上,要特別注意的細節。

下圖也比較了 properties 語法和 yml 語法的差別,提供給大家參考:

總結 #

這篇文章我們先介紹了什麼是 Spring Boot 的設定檔(即是 application.properties 檔案)、以及 properties 語法的使用方法,並且我們也介紹了要如何使用 @Value,去讀取 application.properties 中的值到 Bean 裡面。

那麼到這篇文章為止,有關於 Spring IoC 的介紹就告一個段落了,在這個 Spring IoC 的部分中:

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

所以透過「Day 5~Day 10」的文章介紹,我們就介紹完 Spring IoC 的重要概念了,雖然這些內容沒辦法涵蓋 Spring IoC 的全部細節,不過作為入門來說,算是已經非常足夠的了!

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

補充:本文是擷取自我開設的線上課程 「Java 工程師必備!Spring Boot 零基礎入門」 的內容,如果你想了解更多的 Spring Boot 的用法,歡迎參考課程簡介 (輸入折扣碼「HH202504KU」即可享 85 折優惠)。