賀!此系列文榮獲 2023 iThome 鐵人賽《優選》獎項,正在規劃出書中,感謝大家的支持🙏,同名課程「Java 工程師必備!Spring Boot 零基礎入門」也已在 Hahow 平台上架
哈囉大家好,我是古古
在前幾篇文章中,我們有介紹了創建 Bean 的方法 @Component
、注入 Bean 的方法 @Autowired
、以及指定 Bean 名字的方法 @Qualifier
,所以到目前為止,我們可以說是對 Bean 已經有了更多的認識,現在的我們已經可以成功的在 Spring Boot 程式中運用 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
來幫助我們達成這件事!
@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
在前面有提到,我們是可以在 class 中新增一個方法,然後在該方法上加上 @PostConstruct
,這樣就可以在「該方法裡面」,去寫上初始化 Bean 的程式
不過這個「被加上 @PostConstruct
的方法」,他在宣告上也是有一些格式需要遵守的:
public
void
所以綜合以上 4 點的話,基本上這個「初始化 Bean 的方法」,通常就會長得像是下面這個樣子
public void XXX();
其中 XXX 可以替換成大家喜歡的單字,常見的有 setup、init、initialize 之類的,皆可以拿來使用
在使用 @PostConstruct
去初始化 Bean 的時候,在同一個 class 中,建議一次只讓一個方法加上 @PostConstruct
,不要同時在多個方法上,都加上 @PostConstruct
如果在同一個 class 中,同時有多個方法上面都加上 @PostConstruct
,雖然 Spring Boot 程式仍舊是可以正常運行起來,但是我們無法知道 Spring Boot 會先執行哪一個方法去初始化 Bean,因此可能會造成程式邏輯的錯誤,並且後續也很難統一管理初始化的設定
因此就建議大家,在同一個 class 內,一次只使用一個 @PostConstruct
,統一的去管理初始化的設定,這樣子不管是在維護上還是運作上,都是比較好的做法
上面的例子因為比較簡單,所以有的人可能會覺得「為什麼不直接在宣告 count 變數的同時,把 count 值也設成 5 就好?感覺用 @PostConstrcut
有點多此一舉?」,不過其實在實務上,@PostConstruct
的用途還是滿多的
使用 @PostConstruct
來初始化的強項在於 「@PostConstruct
可以進行複雜的初始化」,譬如說在 Map 裡生成預先定義好的數據、或是取得其他注入的 Bean 的資訊、或是檢查注入的 Bean 是否為 null 值....之類的,這些都是可以在 @PostConstruct
中做到的
因此在實務上,使用 @PostConstruct
來進行 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 設定檔中的值,那麼我們就下一篇文章見啦!
請問不能直接使用建構子來初使 count 的數值嗎?
建構子也是可以拿來初始化的!
只是就如同文中的「補充 1:我們真的需要 @PostConstruct
嗎?」提到的,使用 @PostConstruct
來初始化的強項在於 「@PostConstruct
可以進行複雜的初始化」,所以通常在 Spring Boot 中,還是會優先使用 @PostConstruct
來進行初始化!
建構子不是一樣能進行複雜的初始化嗎? 這樣一說,請教建構子跟 @PostConstruct 的順序,應該是先建構子,等建構完成後,接著才會執行 @PostConstruct 對嗎?
如果建構子也能進行複雜的始化,與 @PostConstruct 的差異是什麼?