4

我是 Spring Framework 的新手,我的问题如下:

我想以DefaultMessageListenerContainer编程方式实例化,我使用的代码是:

DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(cf);
container.setDestination(Queue);
container.setMessageListener(Consumer);
container.setReceiveTimeout(-1);
container.setMaxConcurrentConsumers(15);
container.setConcurrentConsumers(10);
container.start();

为什么在DefaultMessageListenerContainer取消部署项目时必须手动关闭?如果我不手动关闭容器,消费者在我的队列中保持打开状态。

当我尝试手动关闭容器(通过调用container.shutdown())时,程序卡住并且项目无法继续。DefaultMessageListenerContainer如果我在没有给出receiveTimeout关机程序的情况下初始化,则正确执行。有什么问题setReceiveTimeout(-1)吗?

4

4 回答 4

1

receiveTimeout是问题所在。要关闭,容器必须有机会停止侦听队列。如果您的消费者线程有无限超时,它将继续侦听并且永远不会检查容器是否需要关闭。您的容器将占用receiveTimeout关闭时间。如果它是-1,它永远不会关闭。

于 2013-05-23T12:23:59.773 回答
1

您只需手动关闭侦听器,因为您已经以编程方式启动了它!如果您使用 ApplicationContext 从 xml 加载 Spring bean,那么关闭 App Context 将为您关闭所有 bean。

我发现控制 Spring 加载的 bean 的最简单方法是创建一个 servlet,它从 HttpServlet 实现 init() 和 destroy() 方法。Init() 从我的 xml 文件(即名为 spring.xml 的主文件)加载我的 Spring 配置,并缓存 ApplicationContext 对象。然后 destory() 将在 ApplicationContext 上调用 close()。这将关闭/关闭所有 Spring bean(即您的 JMS 侦听器将停止)。

您以编程方式创建侦听器的任何特殊原因?

于 2013-01-28T16:15:18.747 回答
0

您在这里需要的是能够停止容器(而不是关闭它或取消注册它)并能够在您想要的时候重新启动它,所有这些都在运行时。只需使用 .start() 和 .stop(),它们是AbstractJmsListeningContainer我认为继承自的方法。并且不要将它们与 .doStart()、.shutDown() 混合使用...请参阅 spring 文档。

通过 Spring 连接您的侦听器,您可以随时从上下文中获取它并在其上运行 .stop 或 .start 。在 Spring 自动装配期间,您可以将该属性设置autoStartup为 false,并且 listenerContainer 将被初始化但不会在启动时进行任何侦听。

于 2013-03-11T15:09:32.303 回答
0

这就是我动态创建新的Listener并让Spring处理关闭过程 的方法

<beans>
//other beans
<bean id="importReadQueueDestination" class="org.springframework.jndi.JndiObjectFactoryBean"> 
        <property name="jndiName"><value>queue/dummyQueue</value></property> 
        <property name="resourceRef"><value>true</value></property>
    </bean>

    <bean id="importQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean"> 
        <property name="jndiName"><value>ConnectionFactory</value></property> 
        <property name="resourceRef"><value>true</value></property> 
    </bean>

     <bean id="importReadQueueSenderService" class="com.localhost.ImportReadQueueSenderService" scope="prototype"/>

    <!-- this is the Message Driven POJO (MDP) -->
    <bean id="importReadMessageListener" class="com.localhost.listener.ImportReadMessageListener" scope="prototype"/>

    <!-- and this is the message listener container -->
    <bean id="importReadJmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" scope="prototype">
        <property name="connectionFactory" ref="importQueueConnectionFactory" />
        <property name="destination" ref="importReadQueueDestination" />
        <property name="messageListener" ref="importReadMessageListener" />
        <property name="concurrentConsumers" value="1"/>
</bean>

</beans>

在这里,我创建了一个虚拟队列queue/dummyQueue,因为 DMLC 需要设置destinationordestinationName属性。
ImportReadMessageListener扩展 MessageListener。用于创建和缓存动态侦听器的 Java 代码

//Actual queue name where I need to send message. `tenantStore` is obtained from ThreadLocalObject
String queue = tenantStore.getProperty("importReadQueue");
//Obtaining existing senderService for that queue
Object queueSenderService = AppConfigurationManager.getQueueSenderService(queue);
if (queueSenderService != null) {
    ((IImportReadQueueSenderService) queueSenderService).sendObjectMessage(importReadQueueDO);
} else {
    // In-case of call received from new tenant, then dynamically create and cache a separate listener and DMLC for it
        InitialContext ic = new InitialContext();
        Queue destination = (Queue) ic.lookup(queue);
        ConnectionFactory importQueueConnectionFactory =(ConnectionFactory) ServiceContext.getBean("importQueueConnectionFactory");

        JmsTemplate importJmsTemplate=new JmsTemplate(importQueueConnectionFactory);
        importJmsTemplate.setDefaultDestination(destination);

        Object importReadMessageListener = ServiceContext.getBean("importReadMessageListener");

        DefaultMessageListenerContainer dmlc = (DefaultMessageListenerContainer)ServiceContext.getBean("importReadJmsContainer");
        dmlc.setDestination(destination);
    /*  below two steps are extremely important else you won't receive any message.  
        I already wasted a day behind this.*/
    //  https://stackoverflow.com/a/21364885/4800126
        dmlc.afterPropertiesSet();
        dmlc.start();

        IImportReadQueueSenderService importQueueSenderService = (IImportReadQueueSenderService) ServiceContext
                .getBean("importReadQueueSenderService");

        AppConfigurationManager.cacheQueueDetails(queue, dmlc, importQueueSenderService,
                importReadMessageListener);
        importQueueSenderService.setJmsTemplate(importJmsTemplate);
        importQueueSenderService.sendObjectMessage(importReadQueueDO);
    }  

所以现在当你关闭应用程序时,Spring 会自动关闭所有的监听器。

于 2020-05-18T12:06:44.383 回答