博客分类:

 

JMSUtil与Spring JmsTemplate的对比

Author:信仰

Date:2012-4-20

未完待续,截止日期2012-4-20

从以下几方面比较JMSUtil和Spring JmsTemplate

l  对JNDI的支持

l  对ConnectionFactory、Connection、Destination、Session、MessageProducer、MessageConsumer对象的处理

l  对事务的处理

l  不同类型的消息处理

l  异常处理

Spring

JMSUtil

对JNDI的支持

支持,可配置,可编码

支持,只能编码

对ConnectionFactory、

Connection、

Destination、

Session、

MessageProducer、

MessageConsumer对象的处理

配置

编码

对事务的处理

配置

编码

对不同类型消息处理

自动转换

编码转换

异常处理

运行时异常,无需写try…catch

检查异常,必须写try…catch

1.   对JNDI的支持

两者都支持JNDI和非JNDI方式。两者的JNDI名称都是在XML中配置完成的,这一点上两者不存在谁更有优势。

1.1. Spring对JNDI的支持

1.1.1.    Spring JMS 实现

JNDI 查询的 JNDI 模板配置

<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">

<property name="environment">

<props>

<prop key="java.naming.factory.initial">

com.ibm.websphere.naming.WsnInitialContextFactory

</prop>

<prop key="java.naming.provider.url">

iiop://127.0.0.1:2813/

</prop>

</props>

</property>

</bean>

通过 JNDI 配置 JMS 连接工厂

<bean id="internalJmsQueueConnectionFactory"

class="org.springframework.jndi.JndiObjectFactoryBean">

<property name="jndiTemplate">

<ref bean="jndiTemplate"/>

</property>

<property name="jndiName">

<value>MQ_JMS_MANAGER</value>

</property>

</bean>

JMS 模板配置

<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate102">

<property name="connectionFactory">

<ref bean="internalJmsQueueConnectionFactory"/>

</property>

<property name="destinationResolver">

<ref bean="jmsDestinationResolver"/>

</property>

<property name="pubSubDomain">

<value>false</value>

</property>

<property name="receiveTimeout">

<value>20000</value>

</property>

</bean>

 JmsTemplate 绑定到应用程序中

<bean id="jmsSender" class="springexample.client.JMSSender">

<property name="jmsTemplate102">

<ref bean="jmsQueueTemplate"/>

</property>

</bean>

<bean id="jmsReceiver" class="springexample.client.JMSReceiver">

<property name="jmsTemplate102">

<ref bean="jmsQueueTemplate"/>

</property>

</bean>

 JmsTemplate 发送 JMS 消息的 JMSSender

public class JMSSender

{

private JmsTemplate102 jmsTemplate102;

public JmsTemplate102 getJmsTemplate102()

{

return jmsTemplate102;

}

public void setJmsTemplate102(JmsTemplate102 jmsTemplate102)

{

this.jmsTemplate102 = jmsTemplate102;

}

public void sendMesage()

{

jmsTemplate102.send("JMS_RequestResponseQueue", new MessageCreator()

{

public Message createMessage(Session session) throws JMSException

{

return session.createTextMessage("This is a sample message");

}

});

}

}

 JmsTemplate 检索 JMS 消息的 JMSReceiver(同步接收)

public class JMSReceiver

{

private JmsTemplate102 jmsTemplate102;

public JmsTemplate102 getJmsTemplate102()

{

return jmsTemplate102;

}

public void setJmsTemplate102(JmsTemplate102 jmsTemplate102)

{

this.jmsTemplate102 = jmsTemplate102;

}

public void processMessage()

{

Message msg = jmsTemplate102.receive("JMS_RequestResponseQueue");

try

{

TextMessage textMessage = (TextMessage) msg;

if ( msg != null )

{

System.out.println(" Message Received -->" + textMessage.getText());

}

}

catch ( Exception e )

{

e.printStackTrace();

}

}

}

1.2. JMSUtil对JNDI的支持

1.2.1.    JMSUtil的实现

XML配置:

<bean id="MsgData_1.0_0001" class="com.cfcc.tips.common.data.MsgData">

<property name="qcfName"><!— 连接工厂JNDI的名字 -->

<value>${jms.logQueueConFactory}</value>

</property>

<property name="outputQ"><!— 队列名 -->

<value>${jms.errLogQueueName}</value>

</property>

<property name="expirationTime"><!— 过期时间 -->

<value>0</value>

</property>

<property name="confMode"><!— 优先级 -->

<value>1</value>

</property>

</bean>

JMSUtil中发送消息报文和对象报文的方法:

public static void putTextMessage(MsgData msgData, boolean transacted) throwsMessageException

{

……

ConnectionFactory connectionFactory = null;

Connection connection = null;

Session session = null;

Destination destination = null;

MessageProducer messageProducer = null;

try

{

connectionFactory = (ConnectionFactory)

LocalContext.lookup(msgData.getQcfName(), true);

destination = (Destination) LocalContext.lookup(msgData.getOutputQ, false);

connection = connectionFactory.createConnection();

session = connection.createSession(transacted, Session.AUTO_ACKNOWLEDGE);

messageProducer = session.createProducer(destination);

// 修改为 二进制 ,字符集为可配置项,缺省为 GBK

BytesMessage message = session.createBytesMessage();

if (msgData.getExpirationTime() != null)

{

messageProducer

.setTimeToLive(msgData.getExpirationTime().longValue());

}

……

message

.writeBytes(msgData.getMsg()

.getBytes(MtoFactory.getInstance().getCharsetName()));

}

catch ( JMSException e1 )

{

String errorMsg = "发送消息失败:错误码" + e1.getErrorCode() + ",错误原因:" + e1.getMessage();

……

}

catch (NamingException e1)

{

String errorMsg = "发送消息失败,查找对应的资源未找到,错误原因:" + e1.getMessage();

……

}

catch ( UnsupportedEncodingException ue )

{

String errorMsg = "发送消息失败,不支持的字符集: " + MtoFactory.getInstance().getCharsetName();

log.error(errorMsg, ue);

throw new MessageException(errorMsg, ue);

}

catch (RuntimeException e)

{

String errorMsg = "发送消息失败:" + e.getMessage();

……

}

finally

{

if ( connection != null )

{

if( messageProducer != null )

{

try

{

messageProducer.close();

}

catch ( JMSException e2 )

{

log.error("关闭messageProducer异常", e2);

}

}

if( session != null )

{

try

{

session.close();

}

catch ( JMSException e2 )

{

log.error("关闭session异常", e2);

}

}

try

{

connection.close();

}

catch ( JMSException e2 )

{

log.error("关闭connection异常", e2);

}

}

}

}

/**

* 发送一条对象报文,将一个对象放入队列中

*

@param qcfName

*            连接工厂名

@param outputQ

*            队列名

@param obj

*            要发送的对象

@param transacted 是否参与事务

@throws MessageException

*/

public static void putObjectMessage(String qcfName, String outputQ, Serializable obj,boolean transacted) throws MessageException

{

ConnectionFactory connectionFactory = null;

Connection connection = null;

Session session = null;

Destination destination = null;

MessageProducer messageProducer = null;

try

{

connectionFactory = (ConnectionFactory) LocalContext.lookup(qcfName, true);

destination = (Destination) LocalContext.lookup(outputQ, false);

connection = connectionFactory.createConnection();

session = connection.createSession(transacted, Session.AUTO_ACKNOWLEDGE);

messageProducer = session.createProducer(destination);

ObjectMessage objmsg = session.createObjectMessage(obj);

objmsg.setJMSType("transfer");

messageProducer.send(objmsg);

}

catch ( JMSException e1 )

{

String errorMsg = "发送消息失败:错误码" + e1.getErrorCode() + ",错误原因:" + e1.getMessage();

……

}

catch (NamingException e1)

{

String errorMsg = "发送消息失败,查找对应的资源未找到,错误原因:" + e1.getMessage();

……

}

catch ( UnsupportedEncodingException ue )

{

String errorMsg = "发送消息失败,不支持的字符集: " + MtoFactory.getInstance().getCharsetName();

log.error(errorMsg, ue);

throw new MessageException(errorMsg, ue);

}

catch (RuntimeException e)

{

String errorMsg = "发送消息失败:" + e.getMessage();

……

}

finally

{

if ( connection != null )

{

if( messageProducer != null )

{

try

{

messageProducer.close();

}

catch ( JMSException e2 )

{

log.error("关闭messageProducer异常", e2);

}

}

if( session != null )

{

try

{

session.close();

}

catch ( JMSException e2 )

{

log.error("关闭session异常", e2);

}

}

try

{

connection.close();

}

catch ( JMSException e2 )

{

log.error("关闭connection异常", e2);

}

}

}

}

2.  对ConnectionFactory、Connection、Destination、Session、MessageProducer、MessageConsumer的处理

Spring对打开和关闭连接的处理由容器控制,而JMS打开和关闭连接的处理由应用来控制。

2.1. JMSUtil对上述对象的处理

关闭上述对象:

finally

{

if ( connection != null )

{

if( messageProducer != null )

{

try

{

messageProducer.close();

}

catch ( JMSException e2 )

{

log.error("关闭messageProducer异常", e2);

}

}

if( session != null )

{

try

{

session.close();

}

catch ( JMSException e2 )

{

log.error("关闭session异常", e2);

}

}

try

{

connection.close();

}

catch ( JMSException e2 )

{

log.error("关闭connection异常", e2);

}

}

}

3.   对事务的处理

3.1. Spring JMSTemplate对事物的处理方式

3.1.1.    本地事务

Spring为JMS提供了本地事务管理的能力,JMS事务管理器和数据库事务管理器都是PlatformTransactionManager接口的实现类,Spring的org.springframework.jms.connection包提供了用于管理JMS本地事务JmsTransactionManager事务管理器,JMS1.0.2则对应JmsTransactionManager102。

<bean id=”transactionManager”

class=”org.springframework.jms.connection.JmsTransactionManager”>

<property name=”connectionFactory” ref=” internalJmsQueueConnectionFactory”/>

</bean>

在进行标准的Spring事务配置后,就能够管理那些基于JmsTemplate编写的JMS处理类。对于那些未基于JmsTemplate编写的JMS处理类,可以让消息监听器容器对它们进行事务管理。DefaultMessageListenerContainer和ServerSessionMessageListenerContainer都支持通过消息监听器使用JMS事务,不过必须为他们提供一个事务管理器,如下配置:

<!— 事务管理器 -->

<bean id=”transactionManager”

class=”org.springframework.jms.connection.JmsTransactionManager”>

<property name=”connectionFactory” ref=” internalJmsQueueConnectionFactory”/>

</bean>

<!—消息监听器容器 -->

<bean id=”listenerContainer”

class=”org.springframeword.jms.listener.DefaultMessageListenerContainer”>

<property name=”transactionManager” ref=”transactionManager”/>

</bean>

3.1.2.    JTA事务

启用JTA事务后,用户可以让数据库操作、JMS操作以及其它符合JTA标准的操作工作在同一个全局事务中。

对于JMS来说,用户必须从Java EE的JNDI中获取XA支持的JMS ConnectionFactory。

对于Spring配置来说,JTA全局事务和本地事务的差别并不大,用户只需要声明一个JtaTransactionManager事务管理器,将事务委托给Java EE应用服务器就可以了。当然,ConnectionFactory必须使用XA支持的JMSConnectionFactory。

下面是让一个数据源和一个JMSConnectionFactory工作于同一个JTA事务的具体配置:

<jee:jndi-lookup id=”jdbcDataSource” jndi-name=”java:comp/env/jdbc/order”/>

<jee:jndi-lookup id=”jmsConnFactory” jndi-name=”java:comp/env/jms/mail”/>

<bean id=”transactionManageer”

class=”org.springframework.transaction.jta.JtaTransactionManager”/>

3.2. JMSUtil对事物的处理方式

3.2.1.    本地事务

在Session可以控制交易,首选Session要定义成transacted,然后通过调用commit或rollback来提交或者回滚事务。

QueueSession queueSession

= connection.createQueueSession(boolean transacted, int acknowledgeMode);

TopicSession topicSession

= connection.createTopicSession(Boolean transacted, int acknowledgeMode);

Session.commit();

Session.rollback()

注意:如果transacted = true,则acknowledgeMode的值便无关紧要。

3.2.2.    JTA事务

还不太了解……

4.   不同类型的消息的处理

JMS有多钟类型的消息:

l  javax.jms.TextMessage

l  javax.jms.MapMessage

l  javax.jms.ObjectMessage

l  javax.jms.BytesMessage

4.1. Spring使用消息转换器发送/接收消息

Spring将POJO和JMS消息的双向转换工作抽象到MessageConverter中,在JmsTemplate中提供了几个convertAndSend()和receiveAndConvert()方法,这些方法自动使用MessageConverter完成消息的转换工作。

4.1.1.    消息转换器

在org.springframework.jms.support.converter包中,Spring提供了消息转换器的支持,首先看一下MessageConverter接口的两个方法:

l  Object fromMessage(Message message)

l  Message toMessage(Object object, Session session)

MessageConverter接口的目的是为了向调用者屏蔽JMS细节,在JMS之上搭建的一个隔离层,这样调用者可以直接发送和接收POJO,而不是发送和接收JMS相关消息,调用者的程序将得到进一步简化。

JMS消息类型

POJO类型

javax.jms.TextMessage

String

javax.jms.MapMessage

java.util.Map

javax.jms.ObjectMessage

java.io.Serializable

javax.jms.BytesMessage

byte[] bytes

当转换发生错误时,Spring将抛出MessageConversionException异常,该异常是org.springframework.jms.JmsException的子类。

JmsTemplate和JmsTemplate102分别使用SimpleMessageConverter和SimpleMessageConverter102作为默认的消息转换器。用户也可以通过实现MessageConverter定义自己的消息转换器,并在配置JmsTemplate时通过messageConverter属性指定自己的消息转换器。

4.1.2.    发送POJO消息

4.1.2.1.       将POJO简单地映射为Message对象发送

       假设User是一个POJO,其结构如下:

package com.baobaotao.domain;

……

public class User implement Serializable

{

private String username;

private String userId;

private String email;

private int level;

// 省略get/setter

}

这个POJO必须实现Serializable接口,以便可以将对象序列化后作为消息发送,除此以外没有任何其他的要求。

将User作为JMS消息发送的操作仅需要一行简单的代码就可以完成:

package com.baobaotao.jms;

……

import com.baobaotao.domain.User;

public class MessageSender extends JmsGatewaySupport

{

……

public void sendUserMsg(User user)

{

// 发送User对象

super.getJmsTemplate.convertAndSend(“userMsgQ”, user);

}

}

JmsTemplate提供了几个重载版本的convertAndSend()方法:

l  convertAndSend

public void convertAndSend(Object message)

throws JmsException

Send the given object to the default destination, converting the object to a JMS message with a configured MessageConverter.

This will only work with a default destination specified!

Specified by:

convertAndSend in interface JmsOperations

Parameters:

message - the object to convert to a message

Throws:

JmsException - converted checked JMSException to unchecked


l  convertAndSend

public void convertAndSend(Destination destination,

Object message)

throws JmsException

Send the given object to the specified destination, converting the object to a JMS message with a configured MessageConverter.

Specified by:

convertAndSend in interface JmsOperations

Parameters:

destination - the destination to send this message to

message - the object to convert to a message

Throws:

JmsException - converted checked JMSException to unchecked


l  convertAndSend

public void convertAndSend(String destinationName,

Object message)

throws JmsException

Send the given object to the specified destination, converting the object to a JMS message with a configured MessageConverter.

Specified by:

convertAndSend in interface JmsOperations

Parameters:

destinationName - the name of the destination to send this message to (to be resolved to an actual destination by a DestinationResolver)

message - the object to convert to a message

Throws:

JmsException - checked JMSException converted to unchecked


l  convertAndSend

public void convertAndSend(Object message,

MessagePostProcessor postProcessor)

throws JmsException

Send the given object to the default destination, converting the object to a JMS message with a configured MessageConverter. The MessagePostProcessor callback allows for modification of the message after conversion.

This will only work with a default destination specified!

Specified by:

convertAndSend in interface JmsOperations

Parameters:

message - the object to convert to a message

postProcessor - the callback to modify the message

Throws:

JmsException - checked JMSException converted to unchecked


l  convertAndSend

public void convertAndSend(Destination destination,

Object message,

MessagePostProcessor postProcessor)

throws JmsException

Send the given object to the specified destination, converting the object to a JMS message with a configured MessageConverter. The MessagePostProcessor callback allows for modification of the message after conversion.

Specified by:

convertAndSend in interface JmsOperations

Parameters:

destination - the destination to send this message to

message - the object to convert to a message

postProcessor - the callback to modify the message

Throws:

JmsException - checked JMSException converted to unchecked


l  convertAndSend

public void convertAndSend(String destinationName,

Object message,

MessagePostProcessor postProcessor)

throws JmsException

Send the given object to the specified destination, converting the object to a JMS message with a configured MessageConverter. The MessagePostProcessor callback allows for modification of the message after conversion.

Specified by:

convertAndSend in interface JmsOperations

Parameters:

destinationName - the name of the destination to send this message to (to be resolved to an actual destination by a DestinationResolver)

message - the object to convert to a message.

postProcessor - the callback to modify the message

Throws:

JmsException - checked JMSException converted to unchecked

4.1.2.2.       将POJO映射为Message后进行后置处理

通过以上方法发送POJO,JmsTemplate仅会按照简单的映射方式发送JMS消息,如果我们需要在此基础上进一步设置Message的Header和Properties部分的值,一种方法是编写自己的消息转换器达到目的,还有一种更好的方法是使用Spring的org.springframework.jms.core.MessagePostProcessor回调接口。JmsTemplate在使用MessageConverter将POJO转换为JMS消息后以及发送消息前,将调用MessagePostProcessor对JMS消息进行后置加工处理。因此,我们有机会通过一个自定义的MessagePostProcessor回调接口对JMS消息对象进行“修正性”工作。

下面,我们在User转换为ObjectMessage后,为消息对象指定过期时间并设置一个属性:

package com.baobaotao.jms;

……

import org.springframework。jms.core.MessagePostProcessor;

public class MessageSender extends JmsGatewaySupport

{

public void sendUserMsg2(final User user)

{

getJmsTemplate().convertAndSend(“userMsgQ”,

user,

new MessagePostProcessor()

{

public Message postProcessMessage(Message message) throws JMSException

{

message.setJMSExpiration(System.currentTimeMillis() + 60 * 60 * 1000); //设置过期时间:一小时后过期

message.setIntProperty(“level”, user.getLevel()); // 设置一个属性

}

});

}

}

4.1.3.    接收POJO消息

4.2. JMSUtil必须直接实例化不同类型的消息对象

4.2.1.    发送消息

例如,如果要发送的消息是javax.jms.TextMessage类型,代码如下所示:

TextMessage txtMsg = queueSession.createTextMessage(obj);

messageProducer.send(txtMsg);

4.2.2.    接收消息

例如,如果要接收的消息是javax.jms.TextMessage类型,代码如下所示:

Message m = messageConsumer.receive(1000 * 1);

if ( m instanceof TextMessage )

{

TextMessage textMessage = (TextMessage) m;

System.out.println(textMessage.getText());

}

5.   异常处理

5.1. Spring JmsTemplate 运行时异常

JMS的异常都是检查型异常,使用原生JMS API,用户需要提供繁琐的异常捕获代码。而Spring将检查型异常转化为运行期异常。

Spring在org.springframework.jms包为javax.jms包中所有的异常类提供了类型转换的镜像实现。如:org.springframework.jms.IllegalStateException对应javax.jms.IllegalStateExceptipn;org.springframework.jms.InvalidClientIDException对应javax.jms.InvalidClientIDException等。Spring中所有JMS相关的异常都继承于JmsException类。

此外,Spring还提供了一些特定的JMS异常类:

l  DestinationResolutionException:Spring允许通过简单的字符串指定消息地址,DestinationResolver在解析消息地址发生错误时抛出异常;

l  SynchedLocalTransactionFailedException:如果在同步本地事务发生异常时抛出该异常,由于事务已经结束后,再次尝试同步事务时会发生这个异常;

l  UncategorizedJmsException:任何未被归类的其他类型一场。

5.2. JMSUtil检查异常

Spring JMSTemplate 与 JMS 原生API比较的更多相关文章

  1. 一个缓存使用案例:Spring Cache VS Caffeine 原生 API

    最近在学习本地缓存发现,在 Spring 技术栈的开发中,既可以使用 Spring Cache 的注解形式操作缓存,也可用各种缓存方案的原生 API.那么是否 Spring 官方提供的就是最合适的方案 ...

  2. Spring MVC 使用Servlet原生API作为参数

    具体看代码: @RequestMapping("/testServletAPI") public void testServletAPI(HttpServletRequest re ...

  3. Spring MVC 支持的原生API参数

    HttpServletRequest HttpServletResponse HttpSession java.security.Principal Local InputStream OutputS ...

  4. Spring MVC 原生API

    原生API: Servlet环境中一些有用的对象: HttpServletRequest HttpServletResponse HttpSession Reader Writer InputStre ...

  5. 【sping揭秘】24、Spring框架对JMS的集成(无环境版,以后学MQ的时候再隆重介绍)& 任务调度和线程池

    这个我也不是很了解,那么这个需要好好学习一下了 JMS有2种消息域类型 1. point to point 点对点模式 2.发布订阅模式  publish/subscribe Pub/Sub 模式 传 ...

  6. Spring框架之jms源码完全解析

    Spring框架之jms源码完全解析 我们在前两篇文章中介绍了Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Oriented Programmi ...

  7. spring boot整合JMS(ActiveMQ实现)

    pom依赖如下: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="ht ...

  8. ZooKeeper实现配置中心的实例(原生API实现)(转)

    说明:要实现配置中心的例子,可以选择的SDK有很多,原生自带的SDK也是不错的选择.比如使用I0Itec,Spring Boot集成等. 大型应用通常会按业务拆分成一个个业务子系统,这些大大小小的子应 ...

  9. jQuery? 回归JavaScript原生API

    如今技术日新月异,各类框架库也是层次不穷.即便当年漫山红遍的JQuery(让开发者write less, do more,So Perfect!!)如今也有被替代的大势.但JS原生API写法依旧:并且 ...

随机推荐

  1. 吴裕雄--天生自然Numpy库学习笔记:NumPy Ndarray 对象

    NumPy 最重要的一个特点是其 N 维数组对象 ndarray,它是一系列同类型数据的集合,以 0 下标为开始进行集合中元素的索引. ndarray 对象是用于存放同类型元素的多维数组. ndarr ...

  2. 吴裕雄 python 神经网络——TensorFlow 队列操作

    import tensorflow as tf q = tf.FIFOQueue(2, "int32") init = q.enqueue_many(([0, 10],)) x = ...

  3. linux文件或目录属性

    wc(word count)命令的功能:统计指定文件的字节数.字数.行数.,并将统计结果显示输出 命令参数: -c 只显示字节数 -l    只显示行数 -w 只显示字数 od命令:查看二进制文件信息 ...

  4. java判断字符串是否是数字

    正则表达式 代码如下: public static boolean isNum(String num){ return num.matches("(\\s)*([+-])?(([0-9]*\ ...

  5. 4.ORM框架的查询

    创建表对应关系代码如下: from flask import Flask, render_template from flask_sqlalchemy import SQLAlchemy app=Fl ...

  6. 跳转连接转base64

    //配置H5连接 @Value("${h5.domain}") private String h5Domain; // 校验商家端权限 public String checkSta ...

  7. 莫烦 - Pytorch学习笔记 [ 一 ]

    1. Numpy VS Torch #相互转换 np_data = torch_data.numpy() torch_data = torch.from_numpy(np_data) #abs dat ...

  8. 9.2.3 hadoop reduce端连接-分区分组聚合

    1.1.1         reduce端连接-分区分组聚合 reduce端连接则是利用了reduce的分区功能将stationid相同的分到同一个分区,在利用reduce的分组聚合功能,将同一个st ...

  9. docsify简单教程

    简介 一个神奇的文档网站生成器. 简单而轻便(〜18kB压缩) 没有静态构建的HTML文件 多个主题 快速开始 建议docsify-cli全局安装,这有助于本地初始化和预览网站. npm i docs ...

  10. 141、Java内部类之实例化外部类对象

    01. 代码如下: package TIANPAN; class Outer { // 外部类 private static String msg = "Hello World !" ...