在 Day12 內容中,我們學會了用 @Component
、@Service
等 Annotation,讓 Spring Boot 自動幫我們掃描並管理自己寫的類別。這種方式簡單又方便,適用於大多數專案程式碼。
但想像一個場景:
你買了一台現成的 咖啡機 (第三方類別, Third-party Library),這台機器是別人做好的,你不能在上面貼
@Component
的標籤。
但是,你還是希望城堡總管 (Spring IoC Container, 控制反轉容器) 能幫你管理它。
這時候該怎麼辦?我們就需要寫一份「操作說明書」,告訴總管該怎麼準備並管理這台機器。這份「說明書」就是 @Configuration
+ @Bean
。
@Configuration
與 @Bean
當我們不能直接在食材 (類別) 上貼標籤時,就必須自己寫「食譜」,教 Spring 如何製作。
這組合包含兩個 Annotation:
@Configuration
=> 一本食譜書,告訴 Spring:這裡專門定義要管理的 Bean。@Bean
=> 食譜裡的一道菜,告訴 Spring:這個方法會產生一個物件,請幫我管理起來。@Configuration
:這是一本 Bean 的食譜書@Bean
:一道菜的具體作法命名規則:
@Bean(name = "eldenRingGame")
public Game myGame() {
return new Game();
}
在使用的時候,可以搭配 @Qualifier("eldenRingGame")
指定要注入哪一個 Bean。
1. 外部類別 (不能改它)
// 這個類別來自一個 .jar 檔,無法修改
public class Game {
private String name = "艾爾登法環";
public String play() {
return "正在玩 " + name;
}
}
說明:
這個Game
類別代表的是我們無法直接修改的外部類別,可能來自第三方函式庫。我們不能在這個類別上加上@Component
等註解 (Annotation),因為它的原始碼不在我們的控制範圍內。
2. 撰寫設定檔 (食譜書)
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.external.library.Game;
@Configuration // 一本食譜書
public class AppConfig {
@Bean // 一道菜的製作方式
public Game myGame() {
return new Game(); // 只 new 一次,交給 Spring 管理
}
}
說明:
@Configuration
告訴 Spring 這是一個專門用來配置 > Bean 的類別myGame()
方法上的@Bean
告訴 Spring:
- 請執行這個方法一次
- 將回傳的
Game
物件註冊為 Bean- Bean 的預設名稱就是
myGame
- 應用程式啟動時,Spring 會自動執行這個方法,只
new
一次Game
物件
3. 使用 Bean
package com.example.demo.controller;
import com.external.library.Game;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GameController {
private final Game game;
public GameController(Game game) {
this.game = game;
}
@GetMapping("/play")
public String playGame() {
return game.play(); // 「正在玩 艾爾登法環」
}
}
👉 我們沒有自己 new Game()
,而是透過 Spring 取得。
說明:
- 控制器 (Controller) 的建構子需要一個
Game
類別的參數- Spring 發現我們需要一個
Game
類型的 Bean,就會找到我們在AppConfig
中定義的myGame()
方法- 注入的
game
實例是由 Spring 容器管理的單一實例 (Singleton),整個應用程式中只會有這一個- 如果有多個相同類型的 Bean,可以使用
@Qualifier("myGame")
來指定要注入哪一個
手動配置 Bean 的運作流程
@Configuration
類別@Bean
的方法RestTemplate
RestTemplate
是 Spring 早期用來呼叫 API 的工具,常見的配置方式就是用 @Bean
。
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
// 未來可以在這裡加上更多客製化設定
return new RestTemplate();
}
@Bean
public Game myGame() {
return new Game();
}
}
在 Service 裡直接注入使用:
@Service
public class ApiService {
private final RestTemplate restTemplate;
public ApiService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String fetchDataFromApi() {
String url = "https://api.example.com/data";
return restTemplate.getForObject(url, String.class);
}
}
應用程式啟動
↓
讀取 @Configuration 類別
↓
執行 @Bean 方法
↓
取得回傳的物件
↓
將物件註冊為 Bean 放入 IoC 容器
↓
需要時透過 @Autowired 或建構子注入
@Component
還是 @Bean
?特性 | @Component (及衍生 Annotation) |
@Bean |
---|---|---|
使用時機 | 我們自己專案中,可以控制的類別 | 來自外部函式庫、或需要複雜初始化邏輯的類別 |
宣告位置 | 類別 (Class) 上 | 方法 (Method) 上,且需在 @Configuration 類別中 |
控制權 | Spring 幫我們建立物件 | 我們自己寫方法建立物件,再交給 Spring 管理 |
比喻 | 自動化貼標籤:「這個類別本身就是元件」 | 手寫食譜:「這個方法告訴你如何製作元件」 |