Day 9 - Bean 的初始化 - @PostConstruct

古古

2023/11/09


哈囉大家好,我是古古

在前幾篇文章中,我們有介紹了創建 Bean 的方法 @Component、注入 Bean 的方法 @Autowired、以及指定 Bean 名字的方法 @Qualifier,所以到目前為止,我們可以說是對 Bean 已經有了更多的認識,現在的我們已經可以成功的在 Spring Boot 程式中運用 Bean 了!

那麼這篇文章,我們就會深入介紹一下,要如何在創建一個 Bean 出來之後,去初始化這個 Bean 的值

什麼是 Bean 的初始化?

所謂的「Bean 的初始化」,指的是**「在 Bean 被創建出來之後,對這個 Bean 去做一些初始值的設定」**,譬如說把變數的值設定成 5,或是進行一些運算之類的,簡單的說就是對這個 Bean 去做初始的出廠設定就對了

舉個例子來說的話,我們可以來試著改寫一下之前所寫的 HpPrinter class,我們在這個 HpPrinter 裡面去加上一個 count 變數,去計算這台印表機還可以印幾次,所以每當我們 call 一次 print() 方法,這個 count 的數量就要減一,實際程式如下:

@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 上面,去加上一個 @Component,所以 Spring Boot 到時候就會為我們創建一個 hpPrinter 的 Bean 出來,並且存放在 Spring 容器裡面

不過到目前為止,因為我們沒有去設定 Bean 的初始化,因此 Spring Boot 就只會去把這個 Bean 給創建出來,並不會去為裡面的 count 值進行初始化,因此這個 hpPrinter Bean 裡面的 count 值就會是 0

如果我們想要讓 Spring Boot 在創建這個 hpPrinter 出來之後,同時也去為這個 count 變數賦予一個初始值,那麼我們就可以使用 @PostConstruct 來幫助我們達成這件事!

初始化 Bean 的方法:@PostConstruct

@PostConstruct 的用途,就是 「為這個 Bean 去進行初始化」,因此我們就可以透過 @PostConstruct 的功能,去設定這個 Bean 中的變數的初始值了

還是上面那個 HpPrinter 的例子,如果我們想要把 HpPrinter 裡面的 count 變數的值,去初始化成 5 的話,那麼我們就可以這樣做:

我們可以在 HpPrinter 這個 class 裡面,去新增一個新的方法,並且在這個方法上面加上 @PostConstruct,這樣就可以在這個 「有加上 @PostConstruct 的方法中」,去初始化 Bean 的值了

像是我們在 HpPrinter 裡面,就去新增了一個 initialize() 的方法,並且在這個方法上面,去加上了 @PostConstruct 這行程式,因此我們就可以在這個 initialize() 的方法中,去初始化這個 Bean 的值,譬如說我們可以把 count 的值設成 5 之類的

所以到時候,當 Spring Boot 創建完 Bean 時,Spring Boot 就會接著去執行 「有加上 @PostConstruct 的那個方法」(此處指的就是 initialize() 方法),進而去完成 Bean 的初始化!

因此在這個情境下,Spring Boot 就是會去執行 initialize() 方法去進行初始化,將 count 的值設定成 5,所以到時候儲存在 Spring 容器裡面的 hpPrinter Bean,他裡面的 count 變數的值就會是 5

1. 使用 @PostConstruct 的注意事項之一:方法有特定格式

在前面有提到,我們是可以在 class 中新增一個方法,然後在該方法上加上 @PostConstruct,這樣就可以在「該方法裡面」,去寫上初始化 Bean 的程式

不過這個「被加上 @PostConstruct 的方法」,他在宣告上也是有一些格式需要遵守的:

  1. 這個方法必須是 public
  2. 這個方法的返回值必須是 void
  3. 這個方法「不能」有參數
  4. 這個方法的名字可以隨意取,不影響 Spring Boot 運作

所以綜合以上 4 點的話,基本上這個「初始化 Bean 的方法」,通常就會長得像是下面這個樣子

public void XXX();

其中 XXX 可以替換成大家喜歡的單字,常見的有 setup、init、initialize 之類的,皆可以拿來使用

2. 使用 @PostConstruct 的注意事項之二:一個 class 建議只有一個方法加上 @PostConstruct

在使用 @PostConstruct 去初始化 Bean 的時候,在同一個 class 中,建議一次只讓一個方法加上 @PostConstruct,不要同時在多個方法上,都加上 @PostConstruct

如果在同一個 class 中,同時有多個方法上面都加上 @PostConstruct,雖然 Spring Boot 程式仍舊是可以正常運行起來,但是我們無法知道 Spring Boot 會先執行哪一個方法去初始化 Bean,因此可能會造成程式邏輯的錯誤,並且後續也很難統一管理初始化的設定

因此就建議大家,在同一個 class 內,一次只使用一個 @PostConstruct,統一的去管理初始化的設定,這樣子不管是在維護上還是運作上,都是比較好的做法

補充 1:我們真的需要 @PostConstruct 嗎?

上面的例子因為比較簡單,所以有的人可能會覺得「為什麼不直接在宣告 count 變數的同時,把 count 值也設成 5 就好?感覺用 @PostConstrcut 有點多此一舉?」,不過其實在實務上,@PostConstruct 的用途還是滿多的

使用 @PostConstruct 來初始化的強項在於 @PostConstruct 可以進行複雜的初始化」,譬如說在 Map 裡生成預先定義好的數據、或是取得其他注入的 Bean 的資訊、或是檢查注入的 Bean 是否為 null 值….之類的,這些都是可以在 @PostConstruct 中做到的

因此在實務上,使用 @PostConstruct 來進行 Bean 的初始化是很常見的作法~

補充 2:初始化 Bean 的另一種方法

除了可以使用這篇文章所介紹的 @PostConstruct 去初始化 Bean 之外,其實也是有另一種方法可以去初始化 Bean 的,那就是「去實作 InitializingBean interface 裡面的 afterPropertiesSet 方法」,用這種寫法的初始化效果,是和使用 @PostConstruct 一模一樣的

不過因為「去實作 InitializingBean interface」算是比較舊的寫法,因此在此系列文中就沒有特別介紹到這部分,並且在實務上,也會建議大家盡量使用 @PostConstruct 來進行 Bean 的初始化

總結

這篇文章介紹了要如何使用 @PostConstruct,去對 Spring Boot 創建出來的 Bean 進行初始化,所以大家以後就可以透過 @PostConstruct,將存放在 Spring 容器中的 Bean 的值,進行初始的出廠設定了

那到這篇文章為止,我們就算是對 Spring IoC 有了比較多的認識,我們已經了解了什麼是 IoC、DI,什麼又是 Spring 容器和 Bean,也知道要怎麼樣在 Spring Boot 中使用 @Component@Autowired@Qualifier 以及 @PostConstruct 這些註解,去對 Bean 進行創建、注入、以及初始化

那麼下一篇文章,我們會延伸出去介紹,要如何透過 @Value 這個註解,將 Spring Boot 設定檔中的值給讀取到 Bean 裡面,讓我們所寫的 Java 程式可以去運用 Spring Boot 設定檔中的值,那麼我們就下一篇文章見啦!

相關連結