iT邦幫忙

2023 iThome 鐵人賽

DAY 7
3
Software Development

Spring Boot 零基礎入門系列 第 7

Spring Boot 零基礎入門 (7) - Bean 的創建和注入 - @Component、@Autowired

  • 分享至 

  • xImage
  •  

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

哈囉大家好,我是古古

上一篇文章有和大家介紹了 IoC、DI、Bean 這些 Spring IoC 中的重要名詞,那麼這篇文章,我們就實際的到 IntelliJ 中,來練習要如何在 Spring Boot 程式中去創建 Bean、以及要如何將 Bean 去注入到別的 class 中

創建 Bean 的方法:@Component


在 Spring Boot 中,最最最最常見的去創建一個 Bean 的方法,就是在 class 上面加上一行 @Component 的程式,這樣就可以了,Magic!!

所以只要我們將 HpPrinter 改寫成是下面這個樣子(注意在最上面加了一行 @Componenet),就可以將 HpPrinter 變成是一個由 Spring 容器所管理的 Bean 了

https://ithelp.ithome.com.tw/upload/images/20230921/20151036zLO8TMRvVg.png

因此當 Spring Boot 程式運行起來之後,Spring Boot 就會去查看有哪些 class 上面加上了 @Component,然後 Spring Boot 就會去提前去 new 一個 object 出來,並且存放在 Spring 容器裡面,等著其他人後續來跟他借

以此處為例,當 Spring Boot 看到 HpPrinter 上面有加上 @Component 之後,Spring Boot 就會去執行下面這行程式,提前去 new 出一個 HpPrinter 的 object 出來

Printer hpPrinter = new HpPrinter();

並且 Spring Boot 會將 hpPrinter 這個變數,存放在 Spring 容器中,等著後續有其他人來借,因此架構就會長得像是下圖這樣

https://ithelp.ithome.com.tw/upload/images/20230921/201510362qeFvRnpzL.png

也因為使用 @Component 來創建 Bean 可以說是非常神速,只需要在 class 上面加上一行程式就可以完成,因此 @Component 也可以說是在 Spring Boot 中使用頻率最高的註解之一

創建 Bean 的注意事項

在使用 @Component 來創建 Bean 時,有一個特別重要的地方要跟大家說一下,就是 這些被創建出來的 Bean,他們的名字,會是「Class 名的第一個字母轉成小寫」

舉例來說,上面的 HpPrinter class,當我們在這個 class 上面加上一個 @Component 之後,Spring 所生成出來的 Bean 的名字,就會是 hpPrinter

https://ithelp.ithome.com.tw/upload/images/20230921/20151036Y8NhO0z3Kh.png

所以同樣的道理,假設我們今天改成是將 @Component 加在 CanonPrinter class 上面,那 Spring 所生成出來的 Bean,就會是 canonPrinter

因此大家之後在創建 Bean 時,需要記得「Spring 生成出來的 Bean 的名字,會是 Class 名的第一個字母轉成小寫」,這個特性在下一篇文章中就會馬上用到,所以也是一個很重要的特性!

補充:除了在 Class 上面加上 @Component 可以創建 Bean 之外,也是可以使用 @Bean + @Configuration 這種方式去創建 Bean 的,不過因為這部分相對複雜,因此在此系列文中不會特別介紹到

注入 Bean 的方法:@Autowired


了解了創建 Bean 的方法之後,接著我們可以來看一下如何去注入 Bean

要注入 Bean 也是很簡單,只需要在變數上面加上 @Autowired 這行程式,就可以將 Spring 容器中的 Bean 給注入進來了,Magic again!!

https://ithelp.ithome.com.tw/upload/images/20230921/20151036sjejgHBGep.png

不過,在使用 @Autowired 去注入 Bean 進來時,有兩個很重要的限制一定得遵守才行!如果不遵守的話,是沒辦法正常去注入 Bean 進來的

1. 使用 @Autowired 的限制之一:該 Class 也必須為 Bean

當我們想要使用 @Autowired 去注入一個 Bean 進來時,我們自己本身也得變成是由 Spring 容器所管理的 Bean 才可以,這樣子 Spring 容器才有辦法透過 DI (依賴注入),將我們想要的 Bean 給注入進來

所以假設我們是一個 Teacher,然後我們想要使用 @Autowired 把 HpPrinter 這個 Bean 給注入進來的話,那麼 Teacher 本身也必須成為一個 Bean 才可以

因此這時候,我們就必須在 Teacher class 上面加上 @Component,將 Teacher 也變成是一個 Bean,這樣後續就可以將透過 @Autowired,將 HpPrinter 這個 Bean 給注入到 Teacher 裡面了

https://ithelp.ithome.com.tw/upload/images/20230921/20151036QsStBIbgsb.png

補充:所以基本上當我們使用了 Spring Boot 這套框架之後,我們就會盡量把所有的 Class 都變成 Bean,因為這樣才能夠去注入來注入去的,所以在 Spring Boot 程式裡面看到一堆 @Component@Autowired 是很正常的~

2. 使用 @Autowired 的限制之二:根據變數類型尋找 Bean

使用 @Autowired 的第二個限制,即是 @Autowired 在注入 Bean 時,是會根據 「變數的類型來尋找 Bean」

假設今天我們在 Teacher 裡面的 Printer 變數上面加上了 @Autowired,就表示我們想要注入一個 Printer 類型的 Bean,因此 Spring 容器就會看他裡面有沒有 Printer 類型的 Bean,如果有的話,就會把這個 Bean 給注入進去,如果沒有的話,就會出現錯誤並且停止程式

而之所以 HpPrinter 這個 class,可以成功的被注入到 Printer 類型的變數裡面,原因就在於「Java 的多型 (polymorphism)」,使得 HpPrinter class 可以「向上轉型」成 Printer interface,因此就能夠成功的將 hpPrinter 這個 Bean,給注入到 Teacher 這個 class 裡面了!

補充:因為這裡牽涉到 Java 的多型特性,因此不熟悉這部分的人可以先回頭看一下 Java 的多型特性介紹,這個概念會貫穿整個 Spring Boot 開發,因此建議還是要了解一下會比較好!

https://ithelp.ithome.com.tw/upload/images/20230921/20151036BjDcyvQJS4.png

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

那說了這麼多,這裡先來總結一下 @Autowired 的具體用法和限制

想要使用 @Autowired 去注入一個 Bean 時,必須滿足:

  1. 必須要確保「自己也是一個 Bean」(即是有在 class 上面加上 @Component
  2. 並且 @Autowired 是透過「變數的類型」來注入 Bean 的(所以只要 Spring 容器中沒有那個類型的 Bean,就會注入失敗)

只要記住以上兩點,就可以在想要的地方使用 @Autowired,去注入 Spring 容器中的 Bean 進來了!

在 Spring Boot 中練習 @Component 和 @Autowired 的用法


了解了 @Component@Autowired 的概念之後,我們可以實際的到 Spring Boot 中來練習他們的用法

Day 4 - 第一個 Spring Boot 程式 中,我們有去創建了一個 MyController class 出來,所以現在在 Spring Boot 程式中,應該是會存在兩個 class,分別是 DemoApplication 以及 MyController

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

接著我們可以去創建一個 Printer interface 出來,只要在 com.example.demo 這個 package 上點擊右鍵,然後選擇 New,接著選擇 Java class

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

然後在下面選擇 interface,並且上面的名字輸入 Printer,這樣就可以去創建一個 Printer interface 出來

https://ithelp.ithome.com.tw/upload/images/20231018/201510368jWTK1DTDW.png

接著在這個 Printer 的 interface 裡面,去新增一個 print() 方法的宣告

public interface Printer {
    void print(String message);
}

創建好 Printer interface 之後,整個結構會長的像是這個樣子

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

練習創建 Bean 的方法:@Component

接著我們一樣可以在 com.example.demo 底下,再去創建一個 HpPrinter 的 class 出來,並且在 HpPrinter 的 class 中,撰寫以下的程式:

@Component
public class HpPrinter implements Printer {
  
    @Override
    public void print(String message) {
        System.out.println("HP印表機: " + message);
    }
}

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

這裡值得注意的是,我們在 HpPrinter class 的第 5 行處,有添加了一個 @Component 在上面,所以這就表示說,我們將 HpPrinter 變成是一個 「由 Spring 容器所管理的 Bean」

因此到時候當 Spring Boot 運行起來時,就會預先去創建 hpPrinter 這個 Bean 出來,並且存放在 Spring 容器裡面了

練習注入 Bean 的方法:@Autowired

創建好 HpPrinter 這個 Bean 之後,接著我們可以來嘗試把這個 HpPrinter 的 Bean,去注入到我們想要的地方

在這裡大家可以回到 MyController 這個 class,然後在 MyController 裡面,加上如下的程式:

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

在 MyController 這裡,我們先新增了一個 Printer 類型的變數,並且在這個變數上面加上一個 @Autowired,這樣到時候,Spring Boot 就會將「Spring 容器中類型為 Printer 的 Bean,給注入到這個 printer 變數上面」

所以換句話說的話,到時候 Spring Boot 就是會把存放在 Spring 容器中的 hpPrinter Bean,去注入到 MyController 的 printer 變數上,因此到時候這個 printer 變數所存放的,就會是一個 HpPrinter 的 object

而當 printer 變數被注入進來之後,我們就可以在下面的 test() 方法中,去使用 printer 變數中的 print() 方法,嘗試去印出一行 Hello World 出來

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

當我們寫到這裡之後,使用 @Autowired 去注入一個 Bean 進來的程式也就完成了!

補充:MyController 為什麼能使用 @Autowired

在這裡有一個地方要補充一下,就是我們在前面有提到,使用 @Autowired 的 class,本身也得是一個 Bean 才是,但是在上面的程式中,我們明明沒有在 MyController 上面加上 @Component 將他變成 Bean,為什麼 MyController 可以使用 @Autowired 去注入 Bean?

其實 MyController 在這裡也是有偷偷成為一個 Bean 的,而這就是 @RestController 所造成的效果

大家可以先把 @RestController 當成是一個厲害版的 @Component,他不僅可以將 class 變成 Bean,還有更多的功能在裡面,不過總結來說的話,MyController 本身在此處也是一個 Bean 的沒錯!所以他才有能力去使用 @Autowired 去注入 Bean 進來

運行 Spring Boot 程式

上述的程式都寫好之後,我們就可以回到 DemoApplication 這個 class,然後點擊播放鍵,去運行這個 Spring Boot 程式

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

運行起來之後,當看到下方的 console 出現「Started DemoApplication in 1.313 seconds」時,就表示 Spring Boot 程式運行成功了

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

接著我們可以打開 Google 瀏覽器,然後在裡面輸入 http://localhost:8080/test ,然後按下 Enter 鍵

這時候如果頁面中有呈現「Hello World」的字樣的話,就表示請求成功了,我們可以回到 IntelliJ 軟體上來看一下結果

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

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

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

會出現這一行「HP印表機: Hello World」的原因是因為,當我們在瀏覽器輸入 http://localhost:8080/test 時,Spring Boot 會去執行 MyController 裡面的 test() 方法,因此才會執行到 printer.print("Hello World") 這行程式,所以才會在 console 上印出「HP印表機: Hello World」的字串

因此只要看到「HP印表機: Hello World」這一行出現,就表示我們成功的透過 @Component@Autowired,在 Spring Boot 中創建一個 HpPrinter Bean 出來,並且將他注入到 MyController 裡面了!

總結


這篇文章先介紹了創建 Bean 的註解 @Component 的用法,也介紹了注入 Bean 的註解 @Autowired 的用法,最後也實際的在 Spring Boot 中練習了這兩個註解的用法,讓大家了解要如何在 Spring Boot 程式中去創建和注入 Bean

那麼到這邊可能有的人就會有疑惑了,既然 @Autowired 是透過「變數的類型」來注入 Bean,那假設在 Spring 容器中有 2 個以上同樣類型的 Bean 該怎麼辦?這時候 Spring 容器到底是會隨機挑選一個 Bean 來注入,還是會直接兩手一攤報錯?

上面的問題 Spring Boot 都想到了!因此下一篇文章就會接著來介紹另一個註解 @Qualifier,去協助 @Autowired 選擇想要注入的 Bean,那我們就下一篇文章見啦!

相關連結



上一篇
Spring Boot 零基礎入門 (6) - IoC、DI、Bean 的介紹
下一篇
Spring Boot 零基礎入門 (8) - 指定注入的 Bean - @Qualifier
系列文
Spring Boot 零基礎入門30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
凱文大叔
iT邦新手 5 級 ‧ 2023-11-04 09:28:45

講到@RestController時可以看原碼,它又有個標註@Controller
而@Controller的原碼中又有個標註@Component

所以@RestController自然也是@Component

古古 iT邦新手 4 級 ‧ 2024-02-01 18:58:02 檢舉

噢噢感謝補充!確實 @RestController 就是因為自帶 @Component,所以他才能成為一個 Bean 沒錯XD(文章中因為篇幅關係,所以沒有特別展開這邊的介紹,感謝補充~)

0

您好,我想請問HpPrinter是因為有 implements Printer 這個interface,在變成 Bean 之後才能被視別為一種Printer,然後透過在MyController這個Class 中 @Autowired注入的嗎?

古古 iT邦新手 4 級 ‧ 2024-02-01 18:59:29 檢舉

對的唷!完全正確!
這裡就是利用到了 「Java 的多型 (polymorphism)」的特性,所以才可以將 HpPrinter 的類型,轉換成 Printer 這個類型

好的了解,謝謝您的回覆!

我要留言

立即登入留言