我们来详细讲解一下在 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 {
@Override
public 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 项目中最流行和推荐的实践。