
本文探讨在 Telegram Bot 项目中使用构造器注入时出现的 BotService ↔ MessageService 循环依赖问题,分析其潜在风险(如 Spring 初始化失败、BeanCurrentlyInCreationException),并提供基于 @Configuration + setter 注入 的安全解耦方案。
本文探讨在 telegram bot 项目中使用构造器注入时出现的 `botservice ↔ messageservice` 循环依赖问题,分析其潜在风险(如 spring 初始化失败、`beancurrentlyincreationexception`),并提供基于 `@configuration` + `setter 注入` 的安全解耦方案。
在你当前的设计中,BotService 在构造函数内直接 new MessageService(this, userService),而 MessageService 又持有了对 BotService 的强引用——这看似实现了“单实例+职责分离”,实则隐含严重架构隐患:
✅ 问题本质:这不是 Spring 推荐的依赖管理方式。手动 new 实例绕过了 Spring 容器的生命周期管理,导致:
-
MessageService无法被 AOP 增强(如事务、日志); -
@Autowired、@Value等注解在MessageService内失效; - 更关键的是:构造器级双向依赖会触发 Spring 的循环依赖检测机制,在单例 Bean 初始化阶段直接抛出
BeanCurrentlyInCreationException(即使你暂时没遇到,高并发下也极易暴露)。
❌ 原始代码的风险示例:
public BotService(@Value("${token}") String token, UserService userService) {
super(token); // TelegramBotsApi 父类初始化
this.userService = userService;
this.messageService = new MessageService(this, userService); // ❌ 手动 new → 脱离容器管控
}
✅ 推荐方案:配置类 + 分阶段注入(Setter-based Circular Resolution)
利用 Spring 的 @Configuration 类显式控制 Bean 创建顺序,并通过 setter 方法打破构造器闭环,既保持单例语义,又规避容器异常:
@Configuration
public class TelegramBotConfig {
@Bean
public BotService botService(@Value("${token}") String token, UserService userService) {
return new BotService(token, userService); // 构造时不传 messageService
}
@Bean
public MessageService messageService(UserService userService) {
return new MessageService(userService); // 构造时不依赖 botService
}
// 后置注入:确保两个 Bean 均已创建完成后再建立关联
@Bean
public InitializingBean configureBotAndMessageService(
BotService botService,
MessageService messageService) {
botService.setMessageService(messageService);
messageService.setBotService(botService);
return () -> {}; // 空初始化回调
}
}
对应地,需调整你的类定义,移除构造器中的循环依赖参数,改用 setter:
// BotService.java
public class BotService extends TelegramLongPollingBot {
private final UserService userService;
private MessageService messageService; // 不再 final,支持后置注入
public BotService(String token, UserService userService) {
super(token);
this.userService = userService;
}
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
// 其他业务方法中可安全调用
public void handleUpdate(Update update) {
messageService.processUpdate(update); // ✅ 此时 messageService 已注入
}
}
// MessageService.java
public class MessageService {
private final UserService userService;
private BotService botService; // 同样非 final
public MessageService(UserService userService) {
this.userService = userService;
}
public void setBotService(BotService botService) {
this.botService = botService;
}
public void processUpdate(Update update) {
// ... 业务逻辑
botService.execute(new SendMessage(chatId, "Hello")); // ✅ 安全调用
}
}
? 关键优势总结:
- ✅ 完全兼容 Spring 生命周期:所有 Bean 由容器托管,支持 AOP、
@PostConstruct、销毁回调等; - ✅ 线程安全前提保障:
InitializingBean或@PostConstruct回调确保关联建立在所有依赖就绪之后; - ✅ 高负载稳定性:避免因构造器阻塞或初始化顺序错乱导致的执行序列紊乱;
- ✅ 可测试性强:单元测试中可轻松 mock
BotService或MessageService进行隔离验证。
⚠️ 注意事项:
- 切勿在
@PostConstruct方法中调用尚未注入的依赖(如botService.execute()),必须确保setBotService()已执行; - 若
MessageService需要访问BotService的final字段(如token),建议将其提取为独立@Bean(如TelegramBotConfig.token()),而非强耦合实例; - 对于极端性能敏感场景,可考虑将
execute()封装为函数式接口注入,进一步解耦执行上下文。
该方案在保持“逻辑集中于 MessageService”设计初衷的同时,彻底规避了构造器循环依赖陷阱,是 Spring 生态中 Telegram Bot 项目稳健落地的推荐实践。










