我是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)
是否有任何问题?
答案 0 :(得分:1)
receiveTimeout
是问题所在。要关闭,容器必须有机会停止侦听队列。如果您的消费者线程具有无限超时,它将继续侦听并且永远不会检查容器是否需要关闭。您的容器将占用receiveTimeout
以关闭。如果它是-1,它将永远不会关闭。
答案 1 :(得分:0)
您只需手动关闭监听器,因为您已经以编程方式启动它!如果您使用ApplicationContext从xml加载Spring bean,那么关闭App Context将关闭所有bean。
我发现控制Spring加载bean的最简单方法是创建一个从HttpServlet实现init()和destroy()方法的servlet。 Init()从我的xml文件(即名为spring.xml的主文件)加载我的Spring配置,并缓存ApplicationContext对象。然后destory()将在ApplicationContext上调用close()。这将关闭/关闭所有Spring bean(即您的JMS侦听器将被停止)。
您是否以编程方式创建了侦听器?
答案 2 :(得分:0)
这里你需要的是能够停止容器(不要关闭它或取消注册它),并且能够在运行时启动它。只需使用.start()和.stop(),这是我认为从AbstractJmsListeningContainer
继承的方法。不要将它们与.doStart(),. shutDown()混合使用......请参阅spring文档。
通过Spring连接您的侦听器,您可以随时从上下文中获取它并在其上运行.stop或.start。在Spring自动装配期间,您可以将属性autoStartup
设置为false,并且将初始化listenerContainer,但不会在启动时进行任何监听。
答案 3 :(得分: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>
在这里,由于DMLC需要设置queue/dummyQueue
或destination
属性,因此我创建了一个虚拟队列destinationName
。
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将自动关闭所有侦听器。