Spring 关闭过程中的DefaultMessageListenerContainer问题

Spring 关闭过程中的DefaultMessageListenerContainer问题,spring,jms,Spring,Jms,我是Spring框架的新手,我的问题如下: 我想以编程方式实例化DefaultMessageListenerContainer,我使用的代码是: DefaultMessageListenerContainer container = new DefaultMessageListenerContainer(); container.setConnectionFactory(cf); container.setDestination(Queue); container.setMessageListe

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

我想以编程方式实例化
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)
有什么问题吗?

您只需手动关闭侦听器,因为您已经通过编程方式启动了它!如果您使用ApplicationContext从xml加载SpringBean,那么关闭应用程序上下文将为您关闭所有Bean

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


以编程方式创建侦听器有什么特别的原因吗?

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


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

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

这就是我动态创建新的
侦听器的方法,并让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>

因此,现在当您关闭应用程序时,Spring将自动关闭所有侦听器。

谢谢您的回答!我希望以编程方式创建侦听器,因为我希望在运行时更改某些属性。这些属性是:并发使用者、每个队列的最大并发使用者、接收超时。通过这种方式,我可以通过JMX提供属性,并用新值重新启动侦听器。实例化侦听器的类实现SmartLifecycle。因此,侦听器在启动时初始化,在项目取消部署时销毁。问题是,当我在setReceiveTimeout方法中设置-1时,当侦听器尝试关闭时,项目卡住了。查看这个类的Spring文档,我认为您需要调用stop()来停止侦听器,因为doShutdown()似乎只是取消注册JMS使用者,这可能会因为您的使用者不断接收而挂起(由于超时=-1)。我试图在doShutdown()之前停止侦听器,但问题仍然存在。当doShutdown()方法执行时,项目将停止。
//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);
    }