iT邦幫忙

2023 iThome 鐵人賽

DAY 8
1
Software Development

Spring Boot 零基礎入門系列 第 8

Spring Boot 零基礎入門 (8) - 指定注入的 Bean - @Qualifier

  • 分享至 

  • xImage
  •  

賀!此系列文榮獲 2023 iThome 鐵人賽《優選》獎項,正在規劃出書中,感謝大家的支持🙏,同名課程「Java 工程師必備!Spring Boot 零基礎入門」也已在 Hahow 平台上架

哈囉大家好,我是古古

在上一篇文章中,我們有介紹了如何使用 @Component 來創建 Bean,也有介紹要如何使用 @Autowired 來注入 Bean

那麼接著這篇文章,我們就會來介紹,當 Spring 容器中有 2 個以上同樣類型的 Bean 存在時,該怎麼去選擇要注入的 Bean

回顧:注入 Bean 的方法 - @Autowired


在前一篇文章中有介紹到,我們是可以在變數上加上 @Autowired,將想要的 Bean 給注入進來的

不過在使用 @Autowired 來注入 Bean 時,必須滿足以下事項:

  1. 首先必須要確保 「自己也是一個 Bean」(即是有在 class 上面加上 @Component
  2. 並且 @Autowired 是透過 「變數的類型」 來注入 Bean 的

所以在使用 @Autowired 去注入 Bean 進來時,Spring Boot 就是會透過「變數的類型」,去 Spring 容器中尋找是否有類型符合的 Bean,如果有同類型的 Bean 存在時,即可以注入成功,如果沒有 Bean 存在,則注入失敗,Spring Boot 就會報錯並且運行失敗

https://ithelp.ithome.com.tw/upload/images/20230922/20151036yZr9miYF6z.png

像是上面的例子中,因為 Spring 容器中只有 hpPrinter 這個 Bean,且 hpPrinter 又是可以向上轉型成 Printer 類型,所以 Spring Boot 就會判定他們類型符合,因此就能夠注入成功

但是,假設在 Spring 容器中,同時有兩個一樣類型的 Bean 存在,譬如說像是下面這張圖,在 Spring 容器中同時有 hpPrinter 和 canonPrinter 這兩個 Bean 存在,那麼在這個情況下,Spring Boot 會如何運作呢?

https://ithelp.ithome.com.tw/upload/images/20230922/20151036Q0pSsc65VO.png

答案是:Spring Boot 會出現錯誤並且運行失敗,因為 hpPrinter 和 canonPrinter 同時都可以向上轉型成 Printer 類型,所以 Spring Boot 不知道該注入哪一個 Bean,因此錯誤原因就會是「同時有多個同樣類型的 Bean 存在,因此無法選擇要注入哪一個」

而為了解決這個問題,就是 @Qualifier 這個註解登場的時候了!

指定注入的 Bean 的名字:@Qualifier


@Qualifier 的用途,是去指定要注入的 Bean 的「名字」是什麼,進而解決同時有兩個同樣類型的 Bean 存在的問題

所以在一般的情況下,我們是可以直接使用 @Autowired 去注入 Bean 的,但是假設今天有兩個同樣類型的 Bean 存在時,那麼我們在使用 @Autowired 的時候,就必須同時去搭配 @Qualifier 這個註解,去選擇要注入的 Bean 是哪一個

所以簡單的說的話,就是先由 @Autowired 篩選類型、再由 @Qualifier 篩選名字,透過這樣子的連環組合拳來解決這個問題!

還是剛剛的例子,假設目前在 Spring 容器中有兩個 Bean:hpPrinter 和 canonPrinter,這時候如果我們想要指定要注入 hpPrinter 這個 Bean 的話,那麼我們就只要在 Printer 變數上面,再加上一個 @Qualifier,並且在裡面,去指定 要注入的 Bean 的名字 是「hpPrinter」,就可以成功去注入 hpPrinter Bean 了,可喜可賀!

https://ithelp.ithome.com.tw/upload/images/20230922/20151036TkaiRM8RZg.png

1. 使用 @Qualifer 的注意事項之一:必須搭配 @Autowired 一起使用

在使用 @Qualifier 去指定「要注入的 Bean」時,一定要搭配 @Autowired 一起使用,單純使用 @Qualifier 是沒有任何用處的

其實大家也可以直接把 @Qualifier 當成是 @Autowired 小弟這樣,他只是專門在輔助 @Autowired 的,如果沒有 @Autowired 的話,他基本上什麼作用都沒有

2. 使用 @Qualifer 的注意事項之二:指定的是「Bean 的名字」

如前面所介紹的,@Qualifier 是為了解決「多個類型同時存在」的問題而誕生,因此他所指定的是 「Bean 的名字」,也由於 @Qualifier 指定的是 Bean 的名字,因此掌握 Bean 的名字的生成方式就非常的重要!

當我們平常使用 @Component 去創建 Bean 時,這些 Bean 的名字,就會是「Class 名的第一個字母轉成小寫」

所以像是由 HpPrinter class 所生成的 Bean,就會叫做 hpPrinter,由 CanonPrinter 所生成的 Bean,名字就會叫做 canonPrinter

因此大家在使用 @Qualifier 去指定要注入的 Bean 的名字時,記得第一個字母要改成小寫,這樣子才能夠正確的去注入該 Bean 進來!

補充:而有關 Bean 的名字生成機制,可以參考 Day 7 - Bean 的創建和注入 - @Component、@Autowired 的介紹

在 Spring Boot 中練習 @Qualifier 的用法


了解了 @Qualifier 的用法之後,我們也可以實際到 Spring Boot 程式中來應用 @Qualifier

延續上一篇文章的程式,目前在 Spring Boot 程式中已經有一個 Printer interface、以及一個 HpPrinter 的 class,並且在 MyController 裡面,會使用了 @Autowired 去注入一個 Printer 類型的 Bean 進來

https://ithelp.ithome.com.tw/upload/images/20231018/20151036681MNFMg8V.png

由於目前在這個 Spring Boot 程式裡面,只有一個 hpPrinter Bean 有辦法向上轉型成 Printer 類型,因此在 MyController 這裡所注入的,就會是 hpPrinter 這個 Bean

https://ithelp.ithome.com.tw/upload/images/20231018/20151036fgOj6Sqbex.png

要測試 @Qualifier 的用法的話,我們可以嘗試在 com.example.demo 這個 package 裡面,再去新增一個 CanonPrinter 的 class 出來,並且在裡面實作如下的程式:

@Component
public class CanonPrinter implements Printer {

    @Override
    public void print(String message) {
        System.out.println("Canon印表機: " + message);
    }
}

https://ithelp.ithome.com.tw/upload/images/20231018/20151036rgviwX8qkl.png

當我們這樣寫之後,等於是 HpPrinter 和 CanonPrinter 這兩個 class,到時候都會被 Spring Boot 所管理,所以 Spring Boot 到時候就會各 new 出一份 bean,並且存放在 Spring 容器中

https://ithelp.ithome.com.tw/upload/images/20231018/20151036LmumrAyUlt.png

這時候我們回到 MyController 上面看一下的話,就會發現在第 11 行的 printer 變數下面,出現了一個紅色的波浪線

這是因為 IntelliJ 能透過我們所寫的程式,預判到這裡會出現注入的問題(Spring 容器同時存在多個同類型的 Bean),因此提前出現了紅色的波浪線,提示我們此處有錯誤

https://ithelp.ithome.com.tw/upload/images/20231018/20151036o33bQtB9nh.png

如果我們不管這個紅色波浪線,執意要運行 Spring Boot 程式的話,在啟動 Spring Boot 的過程中,console 就會噴出下列的錯誤訊息,提示我們啟動 Spring Boot 失敗

在 console 的錯誤訊息中,有出現一段「MyController required a single bean, but 2 were found」,這一行的訊息就是表示「MyController 想要注入一個 Bean,但是發現 Spring 容器中存在 2 個同樣類型的 Bean,因此注入失敗」

而在下方也會有提示,要「using @Qualifier」來解決這個 multiple Bean 的問題

https://ithelp.ithome.com.tw/upload/images/20231018/20151036W7OEiMZ4wl.png

因此我們可以回到 MyController 上,然後在 printer 變數上面,去加上一行 @Qualifier( "canonPrinter") 的程式,指定要去注入「Bean 的名字為 canonPrinter」的那個 Bean

https://ithelp.ithome.com.tw/upload/images/20231018/20151036xmZiFBIOcO.png

寫好之後可以重新運行一下 Spring Boot,當看到 console 下方出現「Started DemoApplication in 1.135 seconds」,就表示 Spring Boot 程式運行成功了

https://ithelp.ithome.com.tw/upload/images/20231018/201510364XoJG1qzFZ.png

接著我們一樣是可以打開 Google 瀏覽器,然後在裡面輸入 http://localhost:8080/test ,然後按下 Enter 鍵,這時候頁面中有呈現「Hello World」的字樣的話,就表示請求成功了,我們可以接著回到 IntelliJ 軟體上來看一下結果

https://ithelp.ithome.com.tw/upload/images/20231018/20151036W9lD4jQcsc.png

這時回到 IntelliJ 上,就可以在 console 的下方看到一行「Canon印表機: Hello World」

https://ithelp.ithome.com.tw/upload/images/20231018/20151036XQHPc28VPb.png

而會出現這一行「Canon印表機: Hello World」的原因是因為,我們在 MyController 中加上了第 12 行的 @Qualifier("canonPrinter"),去指定了「要注入的 Bean 為 canonPrinter 這個 Bean」,因此到時候 Spring Boot 就會將 canonPrinter 給注入到這個 printer 變數裡面

所以後續在下方第 17 行去執行 printer.print("Hello World") 時,實際上是去執行 canonPrinter 的 print 方法,因此才會在 console 上輸出「Canon印表機: Hello World」

所以只要看到「Canon印表機: Hello World」這一行的出現,也就表示我們成功的透過 @Qualifier,去指定要注入的 Bean 是哪一個了!

補充:大家也可以嘗試把第 17 行修改一下,像是改成 @Qualifier("hpPrinter"),就可以改成是去注入 hpPrinter 那個 Bean 進來,因此在 console 上輸出的,就會改成是「HP印表機: Hello World」

總結


這篇文章介紹了要如何使用 @Qualifier,去指定要注入的 Bean 的名字,進而去輔助 @Autowired 透過變數的類型去注入 Bean 的衍生問題,並且我們也實際的到 Spring Boot 中,練習了 @Qualifier 的用法

而在介紹完 Bean 的創建和注入之後,接著我們會來介紹要如何「初始化」一個 Bean,這也是在實戰中非常常用到的用法,那我們就下一篇文章見啦!

相關連結



上一篇
Spring Boot 零基礎入門 (7) - Bean 的創建和注入 - @Component、@Autowired
下一篇
Spring Boot 零基礎入門 (9) - Bean 的初始化 - @PostConstruct
系列文
Spring Boot 零基礎入門30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言