我們來詳細講解一下在 Spring Boot 中如何使用構造函數注入,并通過一個完整的、可運行的例子來演示。
構造函數注入是 Spring 官方最推薦的依賴注入方式,因為它能保證對象的不可變性和依賴的完整性。
核心理念
在 Spring Boot 中使用構造函數注入非常簡單,你只需要做到:
- 在你的類中,創建一個需要依賴作為參數的構造函數。
- 將依賴字段聲明為
private final
。 - 就這樣,完成了! Spring Boot 會自動檢測到這個構造函數,并為你注入所需的 Bean。
一個關鍵點:自 Spring 4.3 版本以后,如果一個類只有一個構造函數,那么 Spring 會自動用它來進行依賴注入,你不再需要在構造函數上顯式地添加 @Autowired
注解。這讓代碼變得更加干凈。
舉例說明:一個簡單的通知服務
我們來創建一個場景:一個 NotificationController
(通知控制器)需要依賴一個 MessageService
(消息服務)來發送通知。
項目結構
src/main/java/com/example/demo/
├── controller/
│ └── NotificationController.java
├── service/
│ ├── MessageService.java (接口)
│ └── EmailService.java (實現)
└── DemoApplication.java (主啟動類)
第1步:創建依賴接口和實現
首先,我們定義消息服務的接口和它的一個具體實現(比如郵件服務)。
MessageService.java
(接口)
package com.example.demo.service;public interface MessageService {String sendMessage(String message);
}
EmailService.java
(實現類)
這個類會被 Spring 作為一個 Bean 來管理。
package com.example.demo.service;import org.springframework.stereotype.Service;@Service // 1. 標記為 @Service,讓 Spring 掃描并創建一個 Bean
public class EmailService implements MessageService {@Overridepublic String sendMessage(String message) {System.out.println("正在通過郵件發送消息: " + message);return "郵件發送成功: " + message;}
}
第2步:在控制器中使用構造函數注入
現在,我們在 NotificationController
中通過構造函數來注入 MessageService
。
NotificationController.java
(消費者)
這是演示構造函數注入的核心代碼。
package com.example.demo.controller;import com.example.demo.service.MessageService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class NotificationController {// 2. 將依賴聲明為 private final// final 確保了依賴在對象創建后不會被修改private final MessageService messageService;// 3. 定義一個構造函數,并將依賴作為參數傳入// 因為這是唯一的構造函數,所以 @Autowired 注解可以省略public NotificationController(MessageService messageService) {System.out.println("NotificationController 正在被創建,注入 MessageService...");this.messageService = messageService;}@GetMapping("/notify")public String sendNotification() {// 4. 直接使用被注入的依賴return messageService.sendMessage("你好,世界!");}
}
第3步:運行 Spring Boot 應用
你的主啟動類 DemoApplication.java
不需要任何改動。
package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
運行與測試
- 啟動
DemoApplication
。 - 在控制臺,你會看到啟動日志,其中應該包含我們打印的信息:
這證明了 Spring 在創建NotificationController 正在被創建,注入 MessageService...
NotificationController
時調用了它的構造函數。 - 打開瀏覽器或使用 Postman/curl 訪問
http://localhost:8080/notify
。 - 你會看到瀏覽器頁面顯示:
郵件發送成功: 你好,世界!
- 同時,你的應用程序控制臺會打印出:
正在通過郵件發送消息: 你好,世界!
這整個過程完美地演示了構造函數注入。Spring 自動發現了 EmailService
是 MessageService
的一個實現,并在創建 NotificationController
時,通過其構造函數將 EmailService
的實例注入了進去。
進階:使用 Lombok 進一步簡化
在實際項目中,如果依賴很多,手寫構造函數會變得很繁瑣。這時可以使用 Lombok 庫來自動生成構造函數。
-
在你的
pom.xml
中添加 Lombok 依賴。<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional> </dependency>
-
修改
NotificationController
,使用@RequiredArgsConstructor
注解。@RequiredArgsConstructor
會為所有final
的字段,或者標記了@NonNull
的字段,自動生成一個構造函數。package com.example.demo.controller;import com.example.demo.service.MessageService; import lombok.RequiredArgsConstructor; // 導入 Lombok 注解 import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;@RestController @RequiredArgsConstructor // 使用 Lombok 自動生成構造函數 public class NotificationController {// Lombok 會為這個 final 字段生成構造函數參數private final MessageService messageService;// 你不再需要手寫下面的構造函數了!/*public NotificationController(MessageService messageService) {this.messageService = messageService;}*/@GetMapping("/notify")public String sendNotification() {return messageService.sendMessage("你好,世界!(來自Lombok)");} }
使用 Lombok 后,代碼變得更加簡潔,同時保留了構造函數注入的所有優點。這是目前 Spring Boot 項目中最流行和推薦的實踐。