You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot应用循环依赖问题求解:Telegram Bot场景

解决Spring Boot中Telegram Bot的循环依赖问题

这个循环依赖问题很典型——你的AssistantBot(带@Component)和UpdateReceiver(带@Service)通过构造器互相依赖,Spring容器在初始化时没法同时完成两者的实例化,自然就抛出循环依赖的异常了。下面给你几个可行的解决办法,从快速修复到架构优化的都有:

方案1:用Setter注入替代其中一个构造器依赖

Spring对Setter注入的循环依赖支持更好,你可以把其中一个类的构造器依赖改成Setter注入。比如修改UpdateReceiver,只在构造器注入TextMessageHandler和CallbackQueryHandler,用Setter注入AssistantBot:

@Service
public class UpdateReceiver {
    private TextMessageHandler textMessageHandler;
    private CallbackQueryHandler callbackQueryHandler;
    private AssistantBot bot;

    // 构造器只注入非循环的依赖
    public UpdateReceiver(TextMessageHandler textMessageHandler, CallbackQueryHandler callbackQueryHandler) {
        this.textMessageHandler = textMessageHandler;
        this.callbackQueryHandler = callbackQueryHandler;
    }

    // Setter注入Bot,Spring 4.3+可以不用加@Autowired
    public void setBot(AssistantBot bot) {
        this.bot = bot;
    }

    public void handleUpdate(Update update) {
        // 原有逻辑不变
        if (update.hasMessage() && update.getMessage().hasText()){
            textMessageHandler.handleTextMessage(update);
        } else if (update.hasCallbackQuery()) {
            callbackQueryHandler.handleCallbackQuery(update.getCallbackQuery());
        } else {
            bot.sendTextMessage(update.getMessage().getChatId(), "I can't handle this!");
        }
    }
}

这样Spring可以先初始化AssistantBot(它只需要UpdateReceiver的实例,而UpdateReceiver现在可以先通过构造器创建出来),之后再把AssistantBot注入到UpdateReceiver的setBot方法里,完美解决循环问题。

方案2:抽离消息发送服务(推荐,解耦架构)

如果想从根源上解决耦合问题,建议把发送消息的逻辑抽成独立的MessageSender服务,这样UpdateReceiver不再直接依赖AssistantBot,而是依赖专门的发送服务,彻底消除循环依赖:

首先创建MessageSender类:

@Component
@Log4j2
public class MessageSender {
    private final AssistantBot bot;

    public MessageSender(AssistantBot bot) {
        this.bot = bot;
    }

    public synchronized void sendTextMessage(long chatId, String message) {
        SendMessage textMessage = new SendMessage()
                .enableMarkdown(false)
                .setChatId(chatId)
                .setText(message);
        try {
            bot.execute(textMessage);
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }

    // 其他发送消息的方法也移到这里
}

然后修改AssistantBot,移除发送消息的方法,只保留Update转发逻辑:

@Component
@Log4j2
public class AssistantBot extends TelegramLongPollingBot {
    private final UpdateReceiver updateReceiver;

    public AssistantBot(UpdateReceiver updateReceiver) {
        this.updateReceiver = updateReceiver;
    }

    @Override
    public void onUpdateReceived(Update update) {
        updateReceiver.handleUpdate(update);
    }

    // 原来的sendTextMessage等方法已经移到MessageSender,这里删掉
}

最后修改UpdateReceiver,注入MessageSender而非AssistantBot:

@Service
public class UpdateReceiver {
    private TextMessageHandler textMessageHandler;
    private CallbackQueryHandler callbackQueryHandler;
    private MessageSender messageSender;

    public UpdateReceiver(TextMessageHandler textMessageHandler, CallbackQueryHandler callbackQueryHandler, MessageSender messageSender) {
        this.textMessageHandler = textMessageHandler;
        this.callbackQueryHandler = callbackQueryHandler;
        this.messageSender = messageSender;
    }

    public void handleUpdate(Update update) {
        if (update.hasMessage() && update.getMessage().hasText()){
            textMessageHandler.handleTextMessage(update);
        } else if (update.hasCallbackQuery()) {
            callbackQueryHandler.handleCallbackQuery(update.getCallbackQuery());
        } else {
            // 用MessageSender发送消息
            messageSender.sendTextMessage(update.getMessage().getChatId(), "I can't handle this!");
        }
    }
}

这个方案不仅解决了循环依赖,还让代码结构更清晰,符合单一职责原则——AssistantBot专注于接收Update,MessageSender专注于发送消息,UpdateReceiver专注于路由Update,后续维护起来也更方便。

方案3:用@Lazy延迟初始化依赖

如果不想修改太多代码,可以用@Lazy注解让Spring创建一个代理对象,延迟初始化其中一个依赖,从而绕过循环:

比如在AssistantBot的构造器里给UpdateReceiver加@Lazy:

@Component
@Log4j2
public class AssistantBot extends TelegramLongPollingBot {
    private final UpdateReceiver updateReceiver;

    public AssistantBot(@Lazy UpdateReceiver updateReceiver) {
        this.updateReceiver = updateReceiver;
    }

    @Override
    public void onUpdateReceived(Update update) {
        updateReceiver.handleUpdate(update);
    }

    // 发送方法不变
}

或者在UpdateReceiver的构造器里给AssistantBot加@Lazy:

@Service
public class UpdateReceiver {
    private final TextMessageHandler textMessageHandler;
    private final CallbackQueryHandler callbackQueryHandler;
    private final AssistantBot bot;

    public UpdateReceiver(TextMessageHandler textMessageHandler, CallbackQueryHandler callbackQueryHandler, @Lazy AssistantBot bot) {
        this.textMessageHandler = textMessageHandler;
        this.callbackQueryHandler = callbackQueryHandler;
        this.bot = bot;
    }

    // handleUpdate方法不变
}

这个方法最快捷,但只是绕过了循环依赖,并没有解决代码耦合的问题,适合临时修复或者小型项目使用。

内容的提问来源于stack exchange,提问作者takotsubo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 07:27:47