
Spring Boot 应用启动时,被 @RestController、@Service 等注解标记的类作为单例 Bean,其构造函数由 Spring IoC 容器在上下文刷新阶段统一实例化并调用,依赖对象也同步完成注入。
spring boot 应用启动时,被 `@restcontroller`、`@service` 等注解标记的类作为单例 bean,其构造函数由 spring ioc 容器在上下文刷新阶段统一实例化并调用,依赖对象也同步完成注入。
在 Spring Boot 中,只要一个类被标注为 @RestController、@Service、@Component 或 @Configuration 等 stereotype 注解,它就会被 Spring 自动识别为受管 Bean,并纳入 IoC 容器的生命周期管理。构造函数的执行时机,本质上就是该 Bean 的初始化时刻——即 Spring 应用上下文(ApplicationContext)刷新(refresh())过程中,容器按需创建并装配 Bean 的阶段。
构造函数何时被调用?
- ✅ 默认情况下(单例作用域):所有 @RestController、@Service 类均为 @Scope("singleton")(无需显式声明),因此其构造函数仅在应用启动时执行一次,用于创建全局唯一的 Bean 实例。
- ✅ 依赖注入同步发生:如示例中 MessageController 的构造函数接收 RabbitMQProducer 参数,Spring 会确保 RabbitMQProducer Bean 已就绪(已构造、已注入其自身依赖),再调用 MessageController 的构造函数完成依赖注入——这是构造器注入(Constructor Injection)的标准行为,也是 Spring 官方推荐的方式。
- ❌ 不会在每次 HTTP 请求时执行:与 Servlet 容器中的 new XxxServlet() 不同,Spring 不会为每个请求新建 Controller 实例;@RestController 是无状态的单例,构造函数绝不会在 GET /api/v1/publish 调用时重复执行。
示例验证(可选日志观察)
你可以在构造函数中添加日志,直观确认执行时机:
@RestController
@RequestMapping("/api/v1")
public class MessageController {
private final RabbitMQProducer producer;
public MessageController(RabbitMQProducer producer) {
this.producer = producer;
System.out.println("✅ MessageController 构造函数执行 —— 此时 Bean 已创建,producer 已注入");
}
@GetMapping("/publish")
public ResponseEntity<String> sendMessage(@RequestParam("message") String message) {
producer.sendMessage(message);
return ResponseEntity.ok("message sent to the rabbitmq");
}
}启动应用后,控制台将在 Spring 启动日志末尾、Web 服务监听端口前输出该日志,印证其发生在上下文初始化阶段。
注意事项与最佳实践
- ? 构造器注入优于字段注入:它保证依赖不可变(final 字段)、提升测试友好性(可直接 new 实例传参),且明确表达强制依赖关系。
- ? 避免在构造函数中执行耗时或阻塞操作(如连接数据库、发送 HTTP 请求):这会延迟应用启动,甚至导致启动失败。应改用 @PostConstruct 或实现 InitializingBean 接口,在 Bean 完全初始化后执行。
- ? 若需原型作用域(@Scope("prototype")):每次从容器获取该 Bean 时都会调用构造函数(例如通过 applicationContext.getBean(MessageController.class)),但 @RestController 通常不应设为 prototype——Web MVC 框架默认仅从容器中获取一次单例 Controller 实例。
总之,理解构造函数执行时机,本质是理解 Spring Bean 生命周期的第一步:它不是 Java 的普通对象创建,而是 Spring 容器驱动的、受控的、可预测的初始化过程。


















