Spring整合ActiveMQ与Camel时CachingConnectionFactory无法关闭JMS连接求助
问题背景
在Spring Framework 5.3.19、ActiveMQ 5.16.3、Apache Camel 3.14.3环境中,应用关闭时出现无法正常关闭ActiveMQ连接的问题,核心错误日志如下:
2023-03-26 14:22:09,103|WARN|[SpringContextShutdownHook]|org.springframework.jms.connection.CachingConnectionFactory|Could not close shared JMS Connection javax.jms.JMSException: Disposed due to prior exception ... Caused by: org.apache.activemq.transport.TransportDisposedIOException: peer (vm://localhost#1) stopped.
核心原因
问题根源是关闭顺序倒置:ActiveMQ Broker通过自身的ShutdownHook先于Spring容器中的JMS连接工厂、Camel消费者关闭,导致后续Spring销毁连接工厂时,连接已经被Broker强制断开,无法正常关闭;同时Camel的JMS消费者线程还在尝试从已关闭的Broker拉取消息,抛出异常。
可行解决方案
1. 禁用ActiveMQ的ShutdownHook,由Spring控制Broker关闭时机
ActiveMQ默认会启动自己的ShutdownHook来关闭Broker,这会导致它先于Spring的Bean销毁流程执行。在Broker的配置中禁用该Hook,让Spring在销毁Bean时统一关闭Broker:
@Bean public BrokerService activeMQBroker() throws Exception { BrokerService broker = new BrokerService(); // 禁用ActiveMQ自带的ShutdownHook broker.setUseShutdownHook(false); // 其他Broker配置(如VM连接器、持久化设置等) broker.addConnector("vm://localhost"); broker.setPersistent(false); return broker; }
2. 调整Bean销毁顺序,确保JMS组件先于Broker销毁
通过@DependsOn注解让Broker依赖于Camel上下文和JMS连接工厂,这样Spring会先销毁被依赖的组件,再销毁Broker:
@Bean @DependsOn({"camelContext", "cachingConnectionFactory"}) public BrokerService activeMQBroker() throws Exception { // Broker配置 }
3. 手动控制关闭流程,在Spring上下文关闭时按顺序停止组件
实现Spring的ApplicationListener,在上下文关闭事件中按「停止Camel路由 → 关闭JMS连接工厂 → 关闭Broker」的顺序执行:
@Component public class AppShutdownHandler implements ApplicationListener<ContextClosedEvent> { @Autowired private CamelContext camelContext; @Autowired private CachingConnectionFactory jmsConnectionFactory; @Autowired private BrokerService activeMQBroker; @Override public void onApplicationEvent(ContextClosedEvent event) { try { // 停止所有Camel路由,确保JMS消费者不再活跃 camelContext.stop(30); // 设置超时时间,单位秒 // 销毁JMS连接工厂,关闭所有连接 jmsConnectionFactory.destroy(); // 最后关闭ActiveMQ Broker activeMQBroker.stop(); } catch (Exception e) { // 记录错误日志,避免阻塞关闭流程 LoggerFactory.getLogger(getClass()).error("Shutdown error", e); } } }
4. 优化CachingConnectionFactory的异常处理
在CachingConnectionFactory中设置resetConnectionOnException为false,避免连接异常时反复重置连接,同时在销毁时忽略已断开连接的关闭异常:
@Bean public CachingConnectionFactory cachingConnectionFactory() { CachingConnectionFactory factory = new CachingConnectionFactory(); factory.setTargetConnectionFactory(activeMQConnectionFactory()); // 关闭异常时自动重置连接的功能,避免销毁时的无效操作 factory.setResetConnectionOnException(false); return factory; }
验证效果
应用关闭时,日志应显示先停止Camel路由,再关闭JMS连接,最后关闭Broker,无「Could not close shared JMS Connection」和消费者线程的异常。
内容的提问来源于stack exchange,提问作者Visal Acharya

