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

Spring Boot中JMS连接恢复后无法重新注册Bean的问题咨询

解决DefaultMessageListenerContainer在T3服务恢复后无法重新注册Bean的问题

问题背景

使用DefaultMessageListenerContainer通过T3协议连接WebLogic JMS队列,服务正常时运行稳定,但T3地址宕机恢复后,Listener无法重新注册相关Bean,重写Listener也无法在应用启动后完成Bean注册。

原代码

@Bean
public QueueConnectionFactory queueConnectionFactory() {
    Context m_context = getInitialContext();
    QueueConnectionFactory queueConnectionFactory = new JMSConnectionFactory();
    try {
        System.out.println("Connection Factory");
        queueConnectionFactory = (QueueConnectionFactory) m_context
                .lookup("myconnectionfactory");
    } catch (Exception e) {
        System.err.println("Exception Connection Factory goes down");            
    }
    return queueConnectionFactory;
}

@Bean
public Context getInitialContext() {
    try {
        Properties h = new Properties();
        System.out.println("getInitialContext ");
        h.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
        h.put(Context.PROVIDER_URL, "t3://100.21.101.12:7001");
        return new InitialContext(h);
    } catch (Exception e) {
        System.err.println("Error at getInitialContext");
         
    }
    return null;
}
@Bean
public Queue jmsQueue() {
    Context m_context = getInitialContext();
    Queue jmsQueue = new MQQueue();
    try {
        System.out.println("jmsQueue ");
        jmsQueue = (Queue) m_context.lookup("myqueue");
    } catch (Exception e) {
         System.err.println("Error at JMS");
    }
    return jmsQueue;
}


@Bean
public DefaultMessageListenerContainer messageListener() {
    DefaultMessageListenerContainer listener = new DefaultMessageListenerContainer();
    try {
        System.out.println("messageListener ");
        listener.setConcurrentConsumers("4");
        listener.setConnectionFactory((ConnectionFactory) queueConnectionFactory());
        listener.setDestination((Destination) jmsQueue());
        listener.setMessageListener(queueListener());
    } catch (Exception e) {
        System.out.println("Exception >>"+e);            
    }
    return listener;
}

错误日志

2022-08-05 15:16:24.615 ERROR 56480 --- [ssageListener-5] .b.e.s.DefaultMessageListenerContainer : Could not refresh JMS Connection for destination from DefaultMessageListener'queue:///' - retrying using FixedBackOff{interval=5000, currentAttempts=52, maxAttempts=unlimited}. Cause: null

问题根源

当前代码中所有JMS相关Bean(ConnectionFactory、Queue)在Spring容器初始化时就完成了JNDI lookup,一旦T3服务宕机,这些Bean会持有失效的JNDI引用。即使T3服务恢复,Spring容器不会自动重新创建单例Bean,DefaultMessageListenerContainer的重试机制只能尝试重建连接,但使用的还是已失效的ConnectionFactory/Queue对象,导致无法恢复。

解决方案:动态化JNDI lookup,避免持有失效引用

通过自定义动态连接工厂和目标解析器,让每次需要连接或队列时重新执行JNDI lookup,确保拿到T3恢复后的有效对象,配合DefaultMessageListenerContainer的重试机制完成自动恢复。

1. 自定义动态QueueConnectionFactory

创建包装类,每次创建连接时重新执行JNDI lookup,获取最新的ConnectionFactory:

public class DynamicQueueConnectionFactory implements QueueConnectionFactory {
    @Override
    public QueueConnection createQueueConnection() throws JMSException {
        try {
            Context context = createInitialContext();
            QueueConnectionFactory factory = (QueueConnectionFactory) context.lookup("myconnectionfactory");
            return factory.createQueueConnection();
        } catch (NamingException e) {
            throw new JMSException("获取连接工厂失败", e);
        }
    }

    @Override
    public QueueConnection createQueueConnection(String userName, String password) throws JMSException {
        try {
            Context context = createInitialContext();
            QueueConnectionFactory factory = (QueueConnectionFactory) context.lookup("myconnectionfactory");
            return factory.createQueueConnection(userName, password);
        } catch (NamingException e) {
            throw new JMSException("获取连接工厂失败", e);
        }
    }

    private Context createInitialContext() throws NamingException {
        Properties props = new Properties();
        props.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
        props.put(Context.PROVIDER_URL, "t3://100.21.101.12:7001");
        return new InitialContext(props);
    }
}

2. 自定义动态DestinationResolver

实现DestinationResolver,每次需要队列时重新执行JNDI lookup:

@Bean
public DestinationResolver destinationResolver() {
    return new DynamicDestinationResolver() {
        @Override
        public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) throws JMSException {
            try {
                Context context = createInitialContext();
                return (Queue) context.lookup(destinationName);
            } catch (NamingException e) {
                throw new JMSException("获取队列失败", e);
            }
        }

        private Context createInitialContext() throws NamingException {
            Properties props = new Properties();
            props.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
            props.put(Context.PROVIDER_URL, "t3://100.21.101.12:7001");
            return new InitialContext(props);
        }
    };
}

3. 修改DefaultMessageListenerContainer配置

不再直接注入初始化时的Queue Bean,而是通过destinationName和自定义的destinationResolver动态获取队列,同时使用动态连接工厂:

@Bean
public QueueConnectionFactory queueConnectionFactory() {
    return new DynamicQueueConnectionFactory();
}

@Bean
public DefaultMessageListenerContainer messageListener() {
    DefaultMessageListenerContainer listener = new DefaultMessageListenerContainer();
    listener.setConcurrentConsumers(4);
    listener.setConnectionFactory(queueConnectionFactory());
    // 通过名称动态获取队列,配合自定义resolver
    listener.setDestinationName("myqueue");
    listener.setDestinationResolver(destinationResolver());
    listener.setMessageListener(queueListener());
    // 明确配置重试间隔(默认已开启,可按需调整)
    listener.setRecoveryInterval(5000);
    // 允许刷新目标对象
    listener.setRefreshDestination(true);
    return listener;
}

4. 移除原有的单例InitialContext/Queue Bean

删除原有的getInitialContext()和jmsQueue() Bean定义,避免持有失效的JNDI引用。

原理说明

  • 动态连接工厂每次创建连接时都会重新建立JNDI上下文并lookup连接工厂,确保T3恢复后能获取有效实例。
  • 自定义DestinationResolver在每次需要队列时重新lookup,避免使用初始化时的失效队列对象。
  • DefaultMessageListenerContainer的重试机制会在连接失败后定期尝试重建连接,此时会调用动态工厂和解析器获取最新的JMS资源,从而自动恢复监听。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 10:06:45