用Spring 的 JmsTemplate 来接收消息时, 按照其文档:
按照JMS 规范, 如果是在事务内部接收消息, 会忽略sessionAcknowledgeMode 选项, Session.sessionAcknowledgeMode 总是 auto-acknowledge. 如果在事务外部调用,默认是 auto-acknowledge的, 如果需要定义为其他 acknowledge mode, 设置sessionTransacted 和sessionAcknowledgeMode属性. 但是, 当我设置sessionTransacted为false, sessionAcknowledgeMode为 client acknowledge 时, 如果调用消息的 acknowledge 方法, 会抛出异常, 因为每次接收完了消息, JmsTemplate 会将 Session 和 Connection 都关闭. 而且消息也是auto-acknowledge的.
查看JmsTemplate 的原代码,发现:
最终接收消息的代码会进入这个方法:
protected Message doReceive(Session session, MessageConsumer consumer)
这是个 protected 方法, subclass 可以 override 它.
于是跟踪进去看看里面的代码发现:
注意到这句: else if (isClientAcknowledge(session)) {
这就是在事务外部接收消息后的处理, 如果我们设置了Session 为 client acknowledge 模式, JmsTemplate 会在这里acknowledge 接收到的消息的.
刚好可以 override 这部分代码, 嵌入我们自己的处理, 让我们自己来决定是否 acknowledge 接收到的消息.
解决方法:
1)
2) Extend JmsTemplate, override JmsTemplate's method: protected Message doReceive(Session session, MessageConsumer consumer).
3) Sample MessageHandler
4) 测试时, 设置一下sssionTransacted 和 sssionAcknowledgeMode.
/*
Default settings for JMS Sessions are "not transacted" and "auto-acknowledge". As defined by the J2EE specification, the transaction and acknowledgement parameters are ignored when a JMS Session is created inside an active transaction, no matter if a JTA transaction or a Spring-managed transaction. To configure them for native JMS usage, specify appropriate values for the "sessionTransacted" and "sessionAcknowledgeMode" bean properties.
*/
按照JMS 规范, 如果是在事务内部接收消息, 会忽略sessionAcknowledgeMode 选项, Session.sessionAcknowledgeMode 总是 auto-acknowledge. 如果在事务外部调用,默认是 auto-acknowledge的, 如果需要定义为其他 acknowledge mode, 设置sessionTransacted 和sessionAcknowledgeMode属性. 但是, 当我设置sessionTransacted为false, sessionAcknowledgeMode为 client acknowledge 时, 如果调用消息的 acknowledge 方法, 会抛出异常, 因为每次接收完了消息, JmsTemplate 会将 Session 和 Connection 都关闭. 而且消息也是auto-acknowledge的.
查看JmsTemplate 的原代码,发现:
最终接收消息的代码会进入这个方法:
protected Message doReceive(Session session, MessageConsumer consumer)
这是个 protected 方法, subclass 可以 override 它.
于是跟踪进去看看里面的代码发现:
if (session.getTransacted()) {
// Commit necessary - but avoid commit call within a JTA transaction.
if (isSessionLocallyTransacted(session)) {
// Transacted session created by this template -> commit.
JmsUtils.commitIfNecessary(session);
}
}
else if (isClientAcknowledge(session)) {
// Manually acknowledge message, if any.
if (message != null) {
message.acknowledge();
}
}
注意到这句: else if (isClientAcknowledge(session)) {
这就是在事务外部接收消息后的处理, 如果我们设置了Session 为 client acknowledge 模式, JmsTemplate 会在这里acknowledge 接收到的消息的.
刚好可以 override 这部分代码, 嵌入我们自己的处理, 让我们自己来决定是否 acknowledge 接收到的消息.
解决方法:
1)
public interface MessageHandler {
/**
* Process the message, if process finish successfully, return true, otherwise false
* return true if process successfully
*/
boolean processMessage(Message message);
}
2) Extend JmsTemplate, override JmsTemplate's method: protected Message doReceive(Session session, MessageConsumer consumer).
public class MyJmsTemplate extends JmsTemplate {
...
} else if (isClientAcknowledge(session)) {
// Manually acknowledge message if MessageHandler is not null and process message successfully
if (message != null) {
if (messageHandler == null || (messageHandler != null && messageHandler.processMessage(message)))
message.acknowledge();
}
}
3) Sample MessageHandler
public class SampleMessageHandler implements MessageHandler {
@Override
public boolean processMessage(Message message) {
try {
int flag = message.getIntProperty("flag");
return (flag < 2);
} catch (JMSException e) {
e.printStackTrace();
return false;
}
}
}
4) 测试时, 设置一下sssionTransacted 和 sssionAcknowledgeMode.
<bean id="myJmsTemplate" class="com.zero.demo.jms.MyJmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultDestination" ref="authReplyDestination"/>
<property name="sessionTransacted" value="false"/>
<property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE"/>
<property name="messageHandler">
<bean class="com.zero.demo.jms.SampleMessageHandler"/>
</property>
</bean>