什么是JMS?
jms即Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
JMS的优势
当前,CORBA、DCOM、RMI等RPC中间件技术已广泛应用于各个领域。但是面对规模和复杂度都越来越高的分布式系统,这些技术也显示出其局限性:
(1)同步通信:客户发出调用后,必须等待服务对象完成处理并返回结果后才能继续执行;
(2)客户和服务对象的生命周期紧密耦合:客户进程和服务对象进程 都必须正常运行;如果由于服务对象崩溃或者网络故障导致客户的请求不可达,客户会接收到异常;
(3)点对点通信:客户的一次调用只发送给某个单独的目标对 象。
面向消息的中间件(Message Oriented Middleware,MOM)较好的解决了以上问题。发送者将消息发送给消息服务器,消息服务器将消息存放在若干队列中,在合适的时候再将消息转发给接 收者。这种模式下,发送和接收是异步的,发送者无需等待;二者的生命周期未必相同:发送消息的时候接收者不一定运行,接收消息的时候发送者也不一定运行; 一对多通信:对于一个消息可以有多个接收者。
已有的MOM系统包括IBM的MQSeries、Microsoft的MSMQ和BEA的MessageQ等。由于没有一个通用的标准,这些系统很难实现 互操作和无缝连接。
Java Message Service(JMS)是SUN提出的旨在统一各种MOM系统接口的规范,它包含点对点(Point to Point,PTP)和发布/订阅(Publish/Subscribe,pub/sub)两种消息模型,提供可靠消息传输、事务和消息过滤等机制。JMS并不是一个产品而是一个规范,JMS的出现是一个巨大变革。
满足大量应用的需求,运行于多种硬件和操作系统平台,支持分布式计算,支持标准接口和协议。开发人员通过调用中间件提供的大量API,实现异构环境的通信,从而屏蔽异构系统中复杂的操作系统和网络协议。
由于标准接口对于可移植性和标准协议对于互操作性的重要性,中间件已成为许多标准化工作的主要部分。分布式应用软件借助中间件可以在不同的技术之间共享资源。
总的来说,中间件屏蔽了底层操作系统的复杂性,使程序开发人员面对一个简单而统一的开发环境,减少了程序设计的复杂性,将注意力集中与自己的业务上,不必再为程序在不同软件系统上的移植而重复工作,从而大大减少了技术上的负担。
Take in Action
在理解了什么是JMS后,在实践阶段Dreamforce会向你介绍在Spring中整合JMS,同时结合Apache ActiveMQ来实现一个模拟聊天器的功能。
来务场景: A在输入端输入相应消息,消息会异步发送到ActiveMQ,B端会监听这个队列,如果有新数据入队,则接收此消息。
根据本实例,你可以通过简单的修改实现双人或多人自由聊天功能。
在本例中,使用Apache 的ActiveMQ来作为JMS中间件,这是一款相当成熟且开源的产品,得到了许多业内人士的好评。
开发采用Spirng Maven集成开发环境,所有的Jar包通过Maven进行管理 , 如果对Spring Maven集成开发环境配置不熟悉的,可以先看这里:http://dreamforce.me/archives/87
新建一个ApplicationContext-JMS.xml
定义JMS连接工厂
1
2
3
|
<
bean
id
=
"jmsFactory"
class
=
"org.apache.activemq.ActiveMQConnectionFactory"
>
</
bean
>
|
定义JMS Template
1
2
3
|
<
bean
id
=
"jmsTemplate"
class
=
"org.springframework.jms.core.JmsTemplate"
>
<
property
name
=
"connectionFactory"
ref
=
"jmsFactory"
/>
</
bean
>
|
定义消息目的地
1
2
3
|
<
bean
id
=
"dreamDestination"
class
=
"org.apache.activemq.command.ActiveMQQueue"
>
<
constructor-arg
index
=
"0"
value
=
"dream.force"
/>
</
bean
>
|
定义接收监听器
1
2
|
<
bean
id
=
"myTextListener"
class
=
"com.darcy.jms.TextListener"
>
</
bean
>
|
定义一个JMS话题
1
2
3
4
|
<
bean
id
=
"jmsTopic"
class
=
"org.apache.activemq.command.ActiveMQTopic"
autowire
=
"constructor"
>
<
constructor-arg
value
=
"STOCKS.Dreamforce.jms"
/>
</
bean
>
|
定义消费者(接收端)
1
2
3
4
5
6
|
<
bean
id
=
"javaConsumer"
class
=
"org.springframework.jms.listener.DefaultMessageListenerContainer"
>
<
property
name
=
"connectionFactory"
ref
=
"jmsFactory"
/>
<
property
name
=
"destination"
ref
=
"chartDestination"
/>
<
property
name
=
"messageListener"
ref
=
"myTextListener"
/>
</
bean
>
|
定义发布者
1
2
3
4
5
6
|
<
bean
id
=
"publisher"
class
=
"com.darcy.jms.SpringPublish"
>
<
property
name
=
"template"
>
<
ref
local
=
"jmsTemplate"
/>
</
property
>
<
property
name
=
"destinations"
ref
=
"chartDestination"
/>
</
bean
>
|
JAVA核心代码一般由三个部分组成: 监听器(Listener),发布端(Publisher), 消息生产者(Creator)
监听器:
|
package
com.darcy.jms;
import
java.util.Date;
import
javax.jms.JMSException;
import
javax.jms.Message;
import
javax.jms.MessageListener;
import
javax.jms.TextMessage;
import
org.apache.activemq.command.ActiveMQMapMessage;
public
class
TextListener
implements
MessageListener{
public
void
onMessage(Message message) {
ActiveMQMapMessage msg =
null
;
System.out.println(
"ONMessage-----------------"
+message.toString());
try
{
if
(message
instanceof
ActiveMQMapMessage) {
msg = (ActiveMQMapMessage) message;
String sentDate = msg.getString(
"date"
);
String reMessage = msg.getString(
"message"
);
int
sentCount = msg.getInt(
"count"
);
System.out.println(
"-------------New Message Arrival-----------"
+
new
Date());
System.out.println(
"It's "
+sentCount+
" time From Darcy: "
+reMessage+
" ---Send time :"
+ sentDate);
}
}
catch
(JMSException e) {
System.out.println(
"JMSException in onMessage(): "
+ e.toString());
}
catch
(Throwable t) {
System.out.println(
"Exception in onMessage():"
+ t.getMessage());
}
}
}
|
发布端:
|
package
com.darcy.jms;
import
java.util.HashMap;
import
java.util.Scanner;
import
javax.jms.Destination;
import
org.springframework.jms.core.JmsTemplate;
public
class
SpringPublish {
private
JmsTemplate template;
private
Destination[] destinations;
public
void
chart()
{
boolean
chart =
true
;
int
count =
0
;
while
(chart)
{
count ++;
Scanner cin=
new
Scanner(System.in);
System.out.println(
"输入聊天内容,输入N停止聊天"
);
String text=cin.nextLine();
if
(text.equals(
"N"
))
{
chart =
false
;
}
System.out.println(
"我:"
+text);
sendChartMessage(count,text);
}
}
protected
void
sendChartMessage(
int
count , String strMessage)
{
MyMessageCreator creator =
new
MyMessageCreator(count,strMessage);
template.send(destinations[
0
], creator);
}
public
JmsTemplate getTemplate() {
return
template;
}
public
void
setTemplate(JmsTemplate template) {
this
.template = template;
}
public
Destination[] getDestinations() {
return
destinations;
}
public
void
setDestinations(Destination[] destinations) {
this
.destinations = destinations;
}
}
|
消息生产者:
|
package
com.darcy.jms;
import
java.util.Date;
import
javax.jms.JMSException;
import
javax.jms.MapMessage;
import
javax.jms.Message;
import
javax.jms.Session;
import
org.springframework.jms.core.MessageCreator;
public
class
MyMessageCreator
implements
MessageCreator {
private
int
msgNo;
private
String strMessage;
public
MyMessageCreator(
int
no,String paramMessage) {
this
.msgNo = no;
this
.strMessage = paramMessage;
}
public
Message createMessage(Session session)
throws
JMSException {
MapMessage message = session.createMapMessage();
message.setString(
"date"
,
new
Date().toString());
message.setString(
"message"
, strMessage);
message.setInt(
"count"
, msgNo);
return
message;
}
}
|
完成上述的代码,聊天器的功能已经实现了,现在开始写一个TestCase来运行以上的代码
本例的TestCase用例是基于Spring的集成测试环境,同时你要保证将applicationContext-jms.xml纳入Spring的管理。
如果还不清楚如何搭建Spring集成测试环境的童鞋,请移步到这里 搭建Spring集成测试环境
|
package
test.JSM;
import
com.darcy.common.BaseTestCase;
import
com.darcy.jms.SpringPublish;
import
org.springframework.jms.listener.DefaultMessageListenerContainer;
public
class
SpringJmsTestMain
extends
BaseTestCase{
private
SpringPublish publisher;
private
DefaultMessageListenerContainer javaConsumer;
public
SpringPublish getPublisher() {
return
publisher;
}
public
void
setPublisher(SpringPublish publisher) {
this
.publisher = publisher;
}
public
void
testJMS()
{
javaConsumer.start();
publisher.chart();
}
public
void
setJavaConsumer(DefaultMessageListenerContainer javaConsumer) {
this
.javaConsumer = javaConsumer;
}
public
DefaultMessageListenerContainer getJavaConsumer() {
return
javaConsumer;
}
}
|
至此代码书写已经完成。但是在运行之前我们需要启动Apache ActionMQ,以使JAVA端能访问到JMS组件。
下载地址:http://activemq.apache.org/download.html
解压后,进入Conf目录,打开ActiveMQ.xml编辑
1
2
3
|
<
transportConnectors
>
</
transportConnectors
>
|
上面的代码是JMS通信地址,这里需要和Spring配置地址一致
启动ActionMQ, 运行bin目录里的ActionMQ.bat.
同时你也可以进入WEB Console, 默认访问地址为:http://localhost:8161/admin/
运行TestCase,开始聊天吧~~~嘿嘿,不可上面的代码只支持单人聊天模式,接受端只是从JMS获取消息并输出。
附录:
Maven相关Denpendency(JMS)
1
2
3
4
5
6
7
8
9
10
11
|
<
dependency
>
<
groupId
>org.springframework</
groupId
>
<
artifactId
>spring-jms</
artifactId
>
<
version
>3.1.1.RELEASE</
version
>
</
dependency
>
<
dependency
>
<
groupId
>org.apache.geronimo.specs</
groupId
>
<
artifactId
>geronimo-jms_1.1_spec</
artifactId
>
<
version
>1.1</
version
>
</
dependency
>
|
由于各种原因,javax.jms包不能从maven默认的repository上下载,所以请使用这个dependency,当然jboss也提供了这个jms包的下载,请自由选择。