JMS,即Java Message Service,它为Java应用程序提供了一种通用的用于创建、发送、接收以及读取消息的方式;

JMS体系架构

1、 JMS Provider
面向消息中间件的,JMS接口的一个实现。提供者可以是Java平台的JMS实现,也可以是非Java平台的面向消息中间件的适配器;

2、 JMS Client
生产或消费基于消息的Java的应用程序或对象;

3、JMS Producer
创建并发送消息的JMS Client;

4、JMS Consumer
接收消息的JMS Client;

5、JMS Message
包括可以在JMS Client之间传递的数据的对象;

6、JMS Queue
一个容纳那些被发送的等待阅读的消息的区域;

7、JMS Topic
一种支持发送消息给多个订阅者的机制;

JMS消息

JMS消息由以下三部分组成:
1、 消息头 :所有消息的消息头都具体相同的字段,用于JMS Client以及JMS Provider对它们进行区别以及进行消息路由;
下面分别对几个重要的消息头字段及其作用和含义进行说明;
1) JMSDestination
消息发送的目的地(队列或主题);创建消息时可以设置JMSDestination,但是在发送完成时其值会更新为发送方所指定的JMSDestination,也就是说发送前该字段会被忽略;当消息被消费时,该字段的值与在它被发送时被设置的值是相同的;
如下面的例子(文中的例子都是基于Apache Active MQ):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Session session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE);
// 创建2个目的地
Destination destination = session.createQueue( "JMS.DEMO" );
Destination destination2 = session.createQueue( "JMS.DEMO2" );
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage( "Test Message" );
// 设置消息的目的地为destination2
message.setJMSDestination(destination2);
// 发送消息
publisher.send(message);
System.out.println(message.getJMSDestination());

代码中,通过message.setJMSDestination(destination2);设置了message的JMSDestination消息头属性值,我们再看看其输出结果:

1
queue: //JMS.DEMO

通过这个例子可以看出,虽然在发送前设置了消息的目的地,但是发送后消息的目的地被重置了;

2) JMSDeliveryMode
指明消息的传输模式,有两种:
DeliveryMode.PERSISTENT:保证消息仅传一次,JMS Provider服务停止后消息不会丢失;
DeliveryMode.NON_PERSISTENT:消息最多传一次,消息会因JMS Provider停止后丢失;
同JMSDestination一样,在发送前设置的会被忽略;
看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
Session session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue( "JMS.DEMO" );
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 发送PERSISTENT消息
publisher.send(session.createTextMessage( "PERSISTENT MESSAGE" ));
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// 发送PERSISTENT消息
publisher.send(session.createTextMessage( "NON_PERSISTENT MESSAGE" ));

例子中分别发送了一条PERSISTENT的消息和一条NON_PERSISTENT的消息;当Active MQ重启后,启动消费端,收到的消息如下:

1
PERSISTENT MESSAGE

该例子说明,在JMS Provider重启后,NON_PERSISTENT消息丢失了,而PERSISTENT消息能正常被消费者消费;

3) JMSMessageID
由JMS Provider指定的消息的唯一标识符;同上面的字段一样,在发送前设置的会被忽略,在发送完成时,由JMS Provider重置该字段;

4) JMSReplyTo
发送端在发送消息时,可以指定该属性(为一个JMSDestination),表示期望收到客户端的响应;是否响应由消费端决定;
如下面的例子:
发送端:

1
2
3
4
5
6
7
8
9
10
11
12
13
Session session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue( "JMS.DEMO" );
Destination destination2 = session.createQueue( "JMS.DEMO3" );
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage( "Test Message" );
message.setJMSReplyTo(destination2);
// 发送消息
publisher.send(message);

接收端(可以根据情况决定是否需要回复):

1
2
3
4
5
6
7
8
9
10
public void onMessage(Message message) {
     try {
         System.out.println( "Receive message: " + message);
         if (message.getJMSReplyTo() != null ) {
             session.createProducer(message.getJMSReplyTo()).send(session.createTextMessage( "This is a reply to" + message.getJMSReplyTo()));
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
}

5) JMSRedelivered
当消费者收到带有JMSRedelivered的消息头时,表明该消息在过去传输过但没有被确认;
JMS Provider必须对该字段进行设置,当为true时即告知消费者该消息是重传的,消费者需要自行处理重复的消息;

6) JMSExpiration
消息的过期时间,其值为当前时间加上存活时间(毫秒);当存活时间设置为0时,该字段的值也被设置为0,表示永不过期;
消费端在一般情况下都不会接收到过期的消息,但JMS Provider并不保证这一点;
下面的例子说明了如何设置消息的过期时间:

1
2
3
4
5
6
7
8
9
10
11
12
Session session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue( "JMS.DEMO" );
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage( "Test Message" );
// 发送消息
publisher.setTimeToLive( 5000 );
publisher.send(message);

7) JMSPriority
消息的优先级,0代表最低优先级,9代表最高优先级;
一般0~4为普通优先级,5~9为加快优先级;
JMS规范里并没有要求JMS Provider严格按这个优先级来实现,但是尽可能实现加快优先级消息的传输在普通消息的前面;
同JMSDestination一样,该字段在发送前被忽略,在发送完成时重置;

2、 消息属性 :除了前面提到的消息头以外,JMS消息还提供了对“属性值对”的支持,以对消息头进行扩展;消息属性主要用于消息选择器(message selector详见下文);
1) 属性名:
属性名必须服务消息选择器的命名规则;

2) 属性值:
可以是基本类型及其对象类型以及Map、List和String;
下面的例子中,消息带HashMap的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Session session = connection.createSession( false , Session.AUTO_ACKNOWLEDGE);
// 创建目的地
Destination destination = session.createQueue( "JMS.DEMO" );
// 创建生产者
MessageProducer publisher = session.createProducer(destination);
// 设置传输模式
publisher.setDeliveryMode(DeliveryMode.PERSISTENT);
// 创建消息
TextMessage message = session.createTextMessage( "Test Message" );
// 发送消息
message.setObjectProperty( "myProp" , new HashMap() {
         {
             this .put( "key1" , "value1" );
             this .put( "key2" , "value2" );
         }
     });
publisher.send(message);

3) 清除属性:
JMS不能清除单个属性,但可以通过Message.clearProperties()方法清除所有消息属性;

3、 消息体 :JMS提供了5种类型的消息体:
1) StreamMessage:消息体是Java流,写入和读出都是顺序的;
2) MapMessage:消息体包含key-value对,key为String,value为基本类型,可以通过迭代器访问;
3) TextMessage:消息体是String;
4) ObjectMessage:消息体是可序列化的Java对象;
5) BytesMessage:消息体是字节数组;
可以通过message.clearBody()来清除消息体;但在消费端,消息体是只读的,针对消息的写操作都会抛出MessageNotWritableException异常