因为对JMS的了解也只算入门级,有些概念也很模糊,不过,卤煮会尽可能去介绍的。另外,sample code都调试过可以跑。

1.神马是JMS?

jms即Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。——摘自百度百科

2.JMS组成要素

JMS提供者,JMS客户,JMS生产者,JMS消费者,JMS队列和JMS主题。具体啥意思,还是参考百度百科吧,不过,概念讲起来也木有啥意思,还会把人绕晕。多跑几次sample,回来再看概念可能更清楚些哈。另,博文中的JMS提供者以IBM 的WebSphere MQ为例。

3.JMS模型

JMS模型定义一组可供 Java™ 应用程序用于执行消息传递操作的接口。

以下列表概括了主要的JMS接口:

Destination:Destination 对象是应用程序将消息发往的位置和/或应用程序从其接收消息的源。

ConnectionFactory:ConnectionFactory 对象包括连接的一组配置属性。应用程序使用连接工厂来创建连接。

Connection:Connection 对象包括应用程序与消息传递服务器的活动连接。应用程序使用连接来创建会话。

Session:Session 对象是用于发送和接收消息的单个线程上下文。应用程序使用会话来创建消息、消息生产者和消息使用者。会话是事务性或非事务性会话。

Message:Message 对象包括应用程序发送或接收的消息。

MessageProducer:应用程序使用消息生产者将消息发送到目标。

MessageConsumer:应用程序使用消息使用者来接收已发送到目标的消息。

下图是这些对象之间的关系(摘自IBM Info Center)其实,IBM info center中资料多多的呀。

Destination、ConnectionFactory 或 Connection 对象可供多线程应用程序的不同线程并发使用,但是 Session、MessageProducer 或 MessageConsumer 对象不能供不同线程并发使用。确保不并发使用 Session、MessageProducer 或 MessageConsumer 对象的最简单方法是为每个线程创建单独的 Session 对象。

JMS支持两种消息传递样式:

  • 点到点消息传递
  • 发布/预订消息传递

这两类消息传递也被称为消息传递域,且您可以将两类消息传递都组合在一个应用程序中。在点到点域中,目标是队列,而在发布/预订域中,目标是主题。

通过JMS1.1 以前的JMS版本,对点到点域的程序设计使用一组接口和方法,而对发布/预订域的程序设计使用另一组接口和方法。两组接口和方法是相似的,但却各自独立。通过JMS1.1,您可以使用一组公共的支持两类消息传递域的接口和方法。公共接口提供了独立于域的每个消息传递域的视图。下表列出了独立于JMS 域的接口及其相应的特定于域的接口。

特别清楚有木有。

4.开发JMS客户端用于连接WMQ及收发消息

下面的code能跑起来的前提是,本地已安装MQ,且创建好队列管理器和相应的QUEUE,TOPIC。

连接方式一:使用的是IBM对于JMS的实现(要导入包com.ibm.mqjms.jar):

 package com.demo;

 import java.io.UnsupportedEncodingException;

 import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.NamingException; import com.ibm.mq.jms.MQQueueConnectionFactory; public class JmsQueueDemo {
private static Connection conn = null;
private static Session session = null;
private static MessageProducer producer = null;
private static MessageConsumer consumer = null;
private static QueueConnection qConn = null; public static void init() {
// 连接工厂,用com.ibm.mq.jms中的类实现javax.jms中的接口
QueueConnectionFactory qcf = new MQQueueConnectionFactory(); // 设置连接工厂属性
try {
//设置WMQ所在机器的IP
((MQQueueConnectionFactory) qcf).setHostName("localhost");
//设置WMQ上队列管理器名
((MQQueueConnectionFactory) qcf).setQueueManager("TestQM");
//设置WMQ上的通道名
((MQQueueConnectionFactory) qcf).setChannel("SYSTEM.DEF.SVRCONN");
//设置WMQ上的监听端口
((MQQueueConnectionFactory) qcf).setPort(1414); //由连接工厂创建连接
qConn = qcf.createQueueConnection(); //建立异常监听器用于监听连接过程中发生的异常
ExceptionListener exceptionListener = new ExceptionListener(){ //此处可放入更多逻辑,由自己定义
public void onException(JMSException e) {
System.out.println("mq exception");
e.printStackTrace();
System.exit(0);
} };
//在连接上面注册监听器
qConn.setExceptionListener(exceptionListener);
} catch (JMSException e) { e.printStackTrace();
return;
}
} public static void main(String[] args) throws NamingException,
JMSException, UnsupportedEncodingException { init();
sendMessage();
// receiveMessage();
receiveWithListener();
destroy(); } public static void sendMessage() throws JMSException {
boolean transacted = false; // 非事务处理(分别接收或发送消息)[事务处理(全部发送或者全部接收作为一个单元的一组消息)]
session = qConn
.createQueueSession(transacted, Session.AUTO_ACKNOWLEDGE);
// 由session创建要发送到的队列
Queue inputQ = session.createQueue("TestQ"); //由session创建消息发送者
MessageProducer sender = session.createProducer(inputQ); //启动连接
qConn.start(); // 消息由会话创建
TextMessage message = session.createTextMessage();
//设置消息内容
message.setText("this is input message from queue sender");
//这句可有可无的哦,主要用于设置消息属性;方便后面取消息时,取特定类型的消息,如"company='systems'"
message.setStringProperty("company", "systems"); // 发送消息,后面的参数依次为消息的持久性设置,消息的优先级,消息在队列的存活时间,设置为0,表示永不失效
//DeliveryMode为PERSISTENT表示,队列管理器或者WMQ重启后,消息仍在queue中;NON_PERSISTENT意思相反
sender.send(message, DeliveryMode.NON_PERSISTENT, 7, 0);
} //使用listener的方式从queue中取消息,可一次取多条消息出来
public static void receiveWithListener() throws JMSException { boolean transacted = false; // 非事务处理(分别接收或发送消息)[事务处理(全部发送或者全部接收作为一个单元的一组消息)]
session = qConn
.createQueueSession(transacted, Session.AUTO_ACKNOWLEDGE);
//从同一个queue中取消息,此处为使用session创建queue
Queue outputQ = session.createQueue("TestQ");
//使用session创建消息消费者,注意了,后面的那个参数就是消息选择器,用于接收特定类型的消息
consumer = session.createConsumer(outputQ, "company='t-systems'");
//创建消息监听器
MessageListener listener = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
System.out.println("Listener 接收消息:"
+ ((TextMessage) message).getText());
}
} catch (JMSException e) {
e.printStackTrace();
}
}
};
//注册消息监听器
consumer.setMessageListener(listener);
//启动连接
qConn.start();
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//取消息的另一种方式,手动从queue中取消息,一次只能接收一条消息
public static void receiveMessage() throws JMSException,
UnsupportedEncodingException { boolean transacted = false; // 非事务处理(分别接收或发送消息)[事务处理(全部发送或者全部接收作为一个单元的一组消息)]
session = qConn
.createQueueSession(transacted, Session.AUTO_ACKNOWLEDGE);
// 对队列管理器上队列的映射
Queue outputQ = session.createQueue("TestQ");
consumer = session.createConsumer(outputQ);
qConn.start();
//此时,若send的message中,设置了message的属性如,"company='systems'",下面的方法是取不到消息的哈
//要取到消息可屏蔽到send中设置message属性的语句,或使用consumer.receive("company='systems'")
Message msg = consumer.receiveNoWait();
//转换消息格式
if (msg instanceof TextMessage) {
TextMessage message = (TextMessage) msg;
System.out.println("received message from queue is:"
+ message.getText());
} else if (msg instanceof BytesMessage) {
BytesMessage message = (BytesMessage) msg;
byte buff[] = null;
long length = message.getBodyLength();
buff = new byte[(int) length];
message.readBytes(buff);
String textmessage = new String(buff, "UTF-8");
System.out.println("received message from queue is:" + textmessage);
}
}
//销毁资源
public static void destroy() throws JMSException {
if (consumer != null) {
consumer.close();
}
if (producer != null) {
producer.close();
}
if (session != null) {
session.close();
}
if (conn != null) {
conn.close();
} } }

上面的代码是点对点消息发送模式的实现,其实,发布/预定模式的实现也差不多了。

 package com.demo;

 import java.io.UnsupportedEncodingException;

 import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.naming.NamingException; import com.ibm.mq.jms.MQTopicConnectionFactory; public class JmsTopicDemo {
private static Connection conn;
private static Session session;
private static MessageProducer producer;
private static MessageConsumer consumer;
private static TopicConnection tConn = null; public static void init() {
// 连接工厂,用com.ibm.mq.jms中的类实现javax.jms中的接口
TopicConnectionFactory tcf = new MQTopicConnectionFactory(); // 设置连接工厂属性
try {
((MQTopicConnectionFactory) tcf).setHostName("localhost");
((MQTopicConnectionFactory) tcf).setQueueManager("TestQM");
((MQTopicConnectionFactory) tcf).setCCSID(1381);
((MQTopicConnectionFactory) tcf).setChannel("SYSTEM.DEF.SVRCONN");
((MQTopicConnectionFactory) tcf).setPort(1414);
tConn = tcf.createTopicConnection(); ExceptionListener exceptionListener = new ExceptionListener() { // 此处可放入更多逻辑
public void onException(JMSException e) {
System.out.println("mq exception");
e.printStackTrace();
System.exit(0);
} };
tConn.setExceptionListener(exceptionListener);
} catch (JMSException e) { e.printStackTrace();
return;
}
} public static void main(String[] args) throws NamingException,
JMSException, UnsupportedEncodingException, InterruptedException { init();
sendMessage();
receiveMessage();
receiveWithListener();
destroy(); } public static void sendMessage() throws JMSException {
boolean transacted = false; session = tConn
.createTopicSession(transacted, Session.AUTO_ACKNOWLEDGE); Topic inputTopic = session.createTopic("TestT");
MessageProducer sender = session.createProducer(inputTopic);
tConn.start(); TextMessage message = session.createTextMessage();
message.setText("this is input message from topic sender"); sender.send(message, DeliveryMode.PERSISTENT, 7, 0); } public static void receiveWithListener() throws JMSException {
// receive message from mq boolean transacted = false; session = tConn
.createTopicSession(transacted, Session.AUTO_ACKNOWLEDGE);
Topic outputT = session.createTopic("TestT"); consumer = session.createConsumer(outputT); MessageListener listener = new MessageListener() {
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
System.out.println("Listener 接收消息:"
+ ((TextMessage) message).getText());
}
} catch (JMSException e) {
e.printStackTrace();
}
}
};
consumer.setMessageListener(listener);
tConn.start();
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} public static void receiveMessage() throws JMSException,
UnsupportedEncodingException { boolean transacted = false; session = tConn
.createTopicSession(transacted, Session.AUTO_ACKNOWLEDGE); Topic outputT = session.createTopic("TestT");
consumer = session.createConsumer(outputT); tConn.start();
Message msg = consumer.receive(5000);
if (msg instanceof TextMessage) {
TextMessage message = (TextMessage) msg;
System.out.println("received message from topic is:"
+ message.getText());
} else if (msg instanceof BytesMessage) {
BytesMessage message = (BytesMessage) msg;
byte buff[] = null;
long length = message.getBodyLength();
buff = new byte[(int) length];
message.readBytes(buff);
String textmessage = new String(buff, "UTF-8");
System.out.println("received message from topic is:" + textmessage);
}
} public static void destroy() throws JMSException {
if (consumer != null) {
consumer.close();
}
if (producer != null) {
producer.close();
}
if (session != null) {
session.close();
}
if (conn != null) {
conn.close();
} } }

不同点的地方,可能就是从topic中取消息的时候,因为消息不能在topic中驻留,所以,一定要是取的动作已经发生了或准备好,再做发送消息的操作,这时候,才能够从topic中收到发送的消息。怎么理解呢?比如说,queue是个篮子,消息发送出去,就会在篮子里。有时,发送的动作也许早就已经完成,但消息会一直在篮子里。取消息的时候,从篮子中取走即可。而topic则是一个篮球框的那种篮网,消息发送到篮网,如果下面没有接收的东西,就会直接从篮网溜走,你这时再去取消息,就只能是竹篮打水一场空啦。所以,从topic中收消息,一定要再发消息之前先把接的动作准备好的哈。

连接方式二:使用javax.jms中的标准JMS接口(要导入包jms.jar)

使用javax.jms中的标准JMS接口,可以隐藏特定厂商的实现,使代码具有更好的可移植性。开发时,只需要关注javax.jms.QueueConnectionFactory 和 javax.jms.Queue这两个对象即可。通常JMS提供厂商会有自己的工具去构建这两个对象,并存储在JNDI命名空间中。可以从命名空间中检索这两个对象,而不用care是哪个厂商实现滴。

对于WMQ,这两个对象是这样构建滴哈。

1)打开WMQ,找到“JMS受管对象”,右击“添加初始上下文”,如下图选择,记得绑定目录是先创建好的哈。

点击“完成”。

2)在刚建好的“初始上下文”下面,找到”连接工厂“,右击新建“连接工厂”,如下图,

点击”下一步“,到最后一页,在连接里面做如下设置,

选择”基本队列管理器“(应先在WMQ上创建好一个队列管理器,此处选上;另还需先创建好;一个queue,下面要用到滴),完成。

此时我们就有javax.jms.QueueConnectionFactory这个对象了。

3)在新建的”初始上下文“下,找到”目标“,新建”目标“,点”下一步“,到最后一页,做下面的设置,

选择”队列管理器“和”队列“,应先在WMQ上创建好。这时,javax.jms.Queue这个对象也构建好了呢。

OK,完事具备了,下面就是配合JNDI方式实现的标准JMS接口了。

 package com.demo;

 import java.util.Hashtable;

 import javax.jms.*;
import javax.naming.*; public class Tester {
public static void main(String[] args) {
send();
} public static void send() {
try { Hashtable<String, String> environment = new Hashtable<String, String>();
//刚才建的初始上下文哈
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
environment.put(Context.PROVIDER_URL, "file:/D:/JNDI-Directory");
InitialContext initContext = new InitialContext(environment);
//创建的连接工厂哈
ConnectionFactory factory = (ConnectionFactory) initContext
.lookup("TestQM_Q");
//创建的目标哈
Destination destination = (Destination) initContext
.lookup("TestQueue");
initContext.close(); Connection connection = factory.createConnection();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE); MessageProducer sender = session.createProducer(destination);
// Send messages
TextMessage message = session .createTextMessage("hello, this is my first jms sample code~~~"); //记住,message并不是send到“TestQueue”里面去了,而是send到WMQ上与“TestQueue”关联的那个queue里面去了
sender.send(message);
session.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

好了,就介绍到这里了。Happy ending~~~

JMS连接WMQ及收发消息的更多相关文章

  1. Android studio使用smack连接xmpp服务器收发消息

    我使用的是ejabberd16.09的Linux版本,安装教程网上有很多,我在这里只介绍一下Android端连接.登录和收发消息的方法.文章最后附上了我写的一个demo,欢迎大家参考. ejabber ...

  2. ActiveMQ学习笔记(5)——使用Spring JMS收发消息

      摘要 ActiveMQ学习笔记(四)http://my.oschina.net/xiaoxishan/blog/380446 中记录了如何使用原生的方式从ActiveMQ中收发消息.可以看出,每次 ...

  3. ActiveMQ消息队列从入门到实践(4)—使用Spring JMS收发消息

    Java消息服务(Java Message Service ,JMS)是一个Java标准,定义了使用消息代理的通用API .在JMS出现之前,每个消息代理都有私有的API,这就使得不同代理之间的消息代 ...

  4. 从收发消息能力来理解TCP建立连接时的三次握手

    TCP是一个全双工协议,意味着在Client和Server都可以接收和发送数据. 所以,从另一个角度理解建立连接的目的就是要确保双方都要知道对端的收发消息的能力是正常的

  5. 深入浅出 JMS(三) - ActiveMQ 消息传输

    深入浅出 JMS(三) - ActiveMQ 消息传输 一.消息协商器(Message Broker) broke:消息的交换器,就是对消息进行管理的容器.ActiveMQ 可以创建多个 Broker ...

  6. JMS学习八(ActiveMQ消息持久化)

    ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB和LevelDB,还有一种内存存储的方式,由于内存不属于持久化范畴,而且如果使用内存队列,可以考虑使用更合适的产品,如ZeroMQ.所以 ...

  7. 没用过消息队列?一文带你体验RabbitMQ收发消息

    人生终将是场单人旅途,孤独之前是迷茫,孤独过后是成长. 楔子 先给大家说声抱歉,最近一周都没有发文,有一些比较要紧重要的事需要处理. 今天正好得空,本来说准备写SpringIOC相关的东西,但是发现想 ...

  8. 刚体验完RabbitMQ?一文带你SpringBoot+RabbitMQ方式收发消息

    人生终将是场单人旅途,孤独之前是迷茫,孤独过后是成长. 楔子 这篇是消息队列RabbitMQ的第二弹. 上一篇的结尾我也预告了本篇的内容:利用RabbitTemplate和注解进行收发消息,还有一个我 ...

  9. 7.websocket收发消息

    客户端主动向服务端发起websocket连接,服务端接收到连接后通过(握手) 客户端 websocket socket = new WebSocket('ws://127.0.0.1/ws/'); 服 ...

随机推荐

  1. 默认系统为UEFI启动的GPT分区的WIN7(8),如何安装VHD的UEFI WIN8(7)

    默认系统为UEFI启动的GPT分区的WIN7(8),如何安装VHD的UEFI WIN8(7) 情况A:如果默认系统为UEFI启动.GPT分区的WIN7,想安装个VHD的UEFI WIN8.1 1:系统 ...

  2. iOS 6 Passbook 入门 1/2

    http://www.raywenderlich.com/zh-hans/23066/ios-6-passbook-%E5%85%A5%E9%97%A8-12 iOS 6 Passbook 入门 1/ ...

  3. Lucene全文搜索 分组,精确查找,模糊查找

    http://zm603380946.iteye.com/blog/1827318 完全个人理解,如有更好的方法,欢迎一起讨论 LuceneUtils.java package com.zbiti.l ...

  4. java super关键字

    子类用super操作被隐藏的成员变量和方法 Example5_7.java class Sum { int n; float f() { float sum=0; for(int i=1;i<= ...

  5. std::function 测试

    #ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" typedef ...

  6. Django - 模型表单(创建、更新、删除)

    urls.py # /music/alubm/add/ url(r'^album/add/$', views.AlbumCreate.as_view(), name="album-add&q ...

  7. Fragment里面嵌套Fragment的问题

    最近两天做项目时,要在fragment里面嵌套Fragment,最开始使用Fragment的hide,show等方法一直失败,,如图,message是一个fragment,在里面又有两个子fragme ...

  8. ArrayList遍历的同时删除--- 删除还是用迭代器的比较好,其它的都会有问题.

    http://javag.iteye.com/blog/403097 ArrayList遍历的同时删除-- 删除还是用迭代器的比较好,其它的都会有问题.     博客分类: 随笔 Javathread ...

  9. Servlet程序开发-Helloworld

    D:\Workspace\WEB-INF\classes下新建HelloServlet.java文件: package org.lxh.servletdemo ; import java.io.* ; ...

  10. margin:0 auto在ie7浏览器里面无效

    把文件头改成 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w ...