JMS 2.0 中的新内容,第 1 部分:易用性
作者:Nigel Deakin
了解新的易用性特性如何使您可以编写更少的代码。
2013 年 5 月发布
本文是由两部分组成的系列的第一篇文章,假设您基本熟悉 Java 消息服务 (JMS) 1.1,并介绍了 JMS 2.0 中的一些新的易用性特性。在
第 2 部分
中,我们将介绍新增的消息传递特性。
JMS 2.0 于 2013 年 4 月发布,是自 2002 年发布 1.1 版之后对 JMS 规范的第一次更新。有人可能会认为 API 这么长时间没有变化,一定已变得陈旧、渐渐废弃。但如果按不同实现的数量来判断一个 API 标准是否成功的话,JMS 是现有最成功的 API 之一。
在 JMS 2.0 中,我们将重点放在与其他企业 Java 技术中具有同样的易用性改进上。虽然现在 Enterprise JavaBeans、Java 持久性等技术比十年前要容易使用得多,但 JMS 基本保持没变,它有一个成功却相当繁冗的 API。
JMS 2.0 中最大的一个变化是引入了一个新 API 来发送和接收消息,从而减少了开发人员的代码编写量。针对 Java EE 应用服务器上运行的应用程序,这个新的 API 还支持资源注入。这就允许应用服务器负责创建和管理 JMS 对象,进一步简化了应用程序。
JMS 2.0 是 Java EE 7 平台的一部分,可用于 Java EE Web 或 EJB 应用程序,也可独立用于 Java SE 环境。下面我将解释,此处介绍的特性有些只能用于独立环境,其他的则只在 Java EE Web 或 EJB 应用程序中可用。
简化的 API
新 API 被称作
简化的 API
。顾名思义,其目的是比现有 JMS 1.1 API 更简单易用,后者(在意料之中地)现在被称作
经典 API
。
简化的 API 由三个新接口构成:
JMSContext
、
JMSProducer
和
JMSConsumer
:
-
JMSContext
用一个对象替代经典 API 中单独的
Connection
和
Session
对象。
-
JMSProducer
是经典 API 中
MessageProducer
对象的轻量级替代品。它允许使用
方法链
(有时称为
构建器模式
)配置消息传递选项、消息头和消息属性。
-
JMSConsumer
替代经典 API 中的
MessageConsumer
对象,使用方式类似。
开发人员现在可以选择使用经典 API(熟悉的 JMS 1.1 对象
Connection
、
Session
、
MessageProducer
和
MessageConsumer
)或简化的 API(JMS 2.0 中引入的
JMSContext
、
JMSProducer
和
JMSConsumer
对象)。
简化的 API 不仅提供了经典 API 的所有特性,还增加了一些其他特性。经典 API 并未弃用,将作为 JMS 的一部分永久保留。
使用简化的 API 发送消息
JMS 1.1 经典 API 投入使用已逾十载,其效用久经考验。JMS 2.0 简化的 API 在哪些方面表现更好?JMS 2.0 简化的 API 需要的代码较少。
清单 1 显示的简单示例使用经典 API 发送一条文本消息。
public void sendMessageJMS11(ConnectionFactory connectionFactory, Queue queueString text) {
try {
Connection connection = connectionFactory.createConnection();
try {
Session session =connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session.createProducer(queue);
TextMessage textMessage = session.createTextMessage(text);
messageProducer.send(textMessage);
} finally {
connection.close();
} catch (JMSException ex) {
// handle exception (details omitted)
清单 1
现在比较清单 1 与清单 2,清单 2 显示如何使用 JMS 2.0 中简化的 API 执行完全相同的操作:
public void sendMessageJMS20(ConnectionFactory connectionFactory, Queue queue,
String text) {
try (JMSContext context = connectionFactory.createContext();){
context.createProducer().send(queue, text);
} catch (JMSRuntimeException ex) {
// handle exception (details omitted)
清单 2
您可以看到,所需编写的代码量已经大为减少。我们来仔细看看。
-
我们将创建一个
JMSContext
对象,而不是创建单独的
Connection
和
Session
对象。
-
JMS 1.1 版在使用
Connection
后用一个
finally
块对
Connection
调用
close
。在 JMS 2.0 中,
JMSContext
对象也有一个需要在使用后调用的
close
方法。不过,无需在代码中显式调用
close
。
JMSContext
实现了 Java SE 7
java.lang.AutoCloseable
接口。这意味着如果我们在
try
-with-resources 块(也是 Java SE 7 的一个新特性)中创建
JMSContext
,就会在块的结尾处自动调用
close
方法,而无需将其显式添加到代码中。
实际上,所有具有
close
方法的 JMS 接口均已扩展,可以实现
java.lang.AutoCloseable
接口,这样它们就都可用于
try
-with-resources 块。这包括
Connection
和
Session
接口以及
JMSContext
。因此,即使使用经典 API,还是可以从此特性中受益。注意,由于这个改动,JMS 2.0 只能用于 Java SE 7。
-
JMS 1.1 版创建
Session
对象时,它传入参数(
false
和
Session.AUTO_ACKNOWLEDGE
)指明我们希望创建一个非事务性会话,该会话中收到的所有消息都将自动确认。在 JMS 2.0 中,这是默认设置(对于 Java SE 应用程序),因此无需指定任何参数。
如果希望指定其他某种会话模式(本地事务、
CLIENT_ACKNOWLEDGE
或
DUPS_OK_ACKNOWLEDGE
),只需传入一个参数而不是两个参数。
-
无需创建一个
TextMessage
对象并将其正文设置为指定字符串。只需将字符串传入
send
方法。JMS 提供程序将自动创建一个
TextMessage
并将其正文设置为所提供的字符串。
-
JMS 1.1 示例为几乎所有方法抛出的
JMSException
提供了一个
catch
块。JMS 2.0 简化的 API 示例有一个类似的块,但它捕获
JMSRuntimeException
。
简化的 API 的特性之一就是其方法不声明检查到的异常。如果遇到错误情况,将抛出
JMSRuntimeException
。这种新异常是
RuntimeException
的一个子类,意味着无需通过调用方法来显式捕获它,也不必在其
throws
子句中声明它。这与经典 API 正好相反,在经典 API 中几乎声明所有方法以抛出
JMSException
,调用方法必须捕获或自己抛出此异常。
清单 1 和清单 2 均显示
ConnectionFactory
和
Queue
对象已作为参数传入。获得这些对象的方法并未改变,因此我们在这里以及在本文的其他清单中不再赘述。通常,将通过 JNDI 查询从 JNDI 信息库获取这些对象。
使用简化的 API 同步接收消息
清单 3 显示了一个使用 JMS 1.1 同步接收一个
TextMessage
并提取其文本的简单示例。
public String receiveMessageJMS11(ConnectionFactory connectionFactory,Queue queue){
String body=null;
try {
Connection connection = connectionFactory.createConnection();
try {
Session session =connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
MessageConsumer messageConsumer = session.createConsumer(queue);
connection.start();
TextMessage textMessage = TextMessage)messageConsumer.receive();
body = textMessage.getText();
} finally {
connection.close();
} catch (JMSException ex) {
// handle exception (details omitted)
return body;
清单 3
清单 4 显示如何使用 JMS 2.0 中简化的 API 执行完全相同的操作:
public String receiveMessageJMS20(
ConnectionFactory connectionFactory,Queue queue){
String body=null;
try (JMSContext context = connectionFactory.createContext();){
JMSConsumer consumer = session.createConsumer(queue);
body = consumer.receiveBody(String.class);
} catch (JMSRuntimeException ex) {
// handle exception (details omitted)
return body;
清单 4
与发送消息一样,也减少了所需编写的代码量。部分原因与前面的示例一样:
-
我们将创建一个
JMSContext
对象,而不是创建单独的
Connection
和
Session
对象。
-
我们可以在
try
-with-resources 块中创建
JMSContext
,这样就可以在块结束时自动关闭它。因此无需调用
close
。
-
我们无需指明我们希望自动确认收到的消息,因为默认设置就是这样。
而且,JMS 2.0 还通过其他两种方式减少了接收消息所需的代码量:
-
在 JMS 1.1 中,我们需要调用
connection.start()
启动将消息传递给使用者,在 JMS 2.0 简化的 API 中则无需这样:连接会自动启动。(如果需要,可以禁用此行为。)
-
无需接收
Message
对象、将其转换成
TextMessage
,然后再调用
getText
提取消息正文。而是调用
receiveBody
,它会直接返回消息正文。
使用简化的 API 异步接收消息
上面的示例显示了如何通过调用一个方法来同步接收消息,该方法在收到消息或发生超时前一直处于阻塞状态。
如果需要在 Java SE 应用程序中异步接收消息,在 JMS 1.1 中,您需要创建一个
MessageConsumer
对象,然后使用
setMessageListener
方法指定实现
MessageListener
接口的对象。然后需要调用
connection.start()
开始传递消息:
MessageConsumer messageConsumer = session.createConsumer(queue);
messageConsumer.setMessageListener(messageListener);
connection.start();
JMS 2.0 简化的 API 代码与此类似。您需要创建一个
JMSConsumer
对象,然后使用
setMessageListener
方法指定实现
MessageListener
接口的对象。消息传递将自动启动;无需调用
start
。
JMSConsumer consumer = context.createConsumer(queue);
consumer.setMessageListener(messageListener);
注意,如果需要在 Java EE 7 Web 或 EJB 应用程序中异步接收消息,则与先前版本的 Java EE 一样,需要使用消息驱动的 bean,而不是
setMessageListener
方法。
将
JMSContext
注入 Java EE 应用程序
如果要编写 Java EE Web 或 EJB 应用程序,那么使用 JMS 2.0 简化的 API 比在 Java SE 还要轻松。这是因为现在可以将
JMSContext
“注入”代码中,让应用服务器来决定何时创建、何时关闭该对象。
以下代码是 Java EE 7 会话 bean 或 servlet 的一个片段,注入
JMSContext
并使用它发送一条消息:
@Inject @JMSConnectionFactory(
"jms/connectionFactory") private JMSContext context;
@Resource(lookup = "jms/dataQueue") private Queue dataQueue;
public void sendMessageJavaEE7(String body) {
context.send(dataQueue, body);
您可以看到,没有创建和关闭
JMSContext
的代码。我们只是声明一个
JMSContext
类型的字段,并添加
@Inject
和
@JMSConnectionFactory
批注。
@Inject
批注告诉容器在需要时创建
JMSContext
。
@JMSConnectionFactory
批注告诉容器应使用的
ConnectionFactory
的 JNDI 名称。
如果在 JTA 事务(无论是容器托管还是 bean 托管)中使用注入的
JMSContext
,则认为
JMSContext
具有
事务作用域
。这意味着提交 JTA 事务后将自动关闭
JMSContext
。
如果在无 JTA 事务时使用注入的
JMSContext
,则认为
JMSContext
具有
请求作用域
。这意味着
JMSContext
将在请求结束时关闭。请求的长度在上下文和依赖注入 (CDI) 规范中定义,通常与来自客户端的 HTTP 请求或消息驱动的 bean 收到的消息有关。
除了由应用服务器自动创建和关闭之外,注入的
JMSContext
还有一些强大的特性。最重要的是,如果 servlet 调用一个会话 bean,或者一个会话 bean 调用另一个会话 bean,二者均使用注入的
JMSContext
,只要两个注入的
JMSContext
对象是用相同方式定义的(例如,具有相同的
JMSConnectionFactory
批注),它们将实际对应于同一
JMSContext
对象。这就减少了应用程序所用 JMS 连接的数量。
JMS 2.0 中的其他 API 简化
JMS 2.0 还提供了另外几种简化。
直接从消息提取正文的新方法
JMS 消息由三部分组成:
不同的消息类型有不同类型的正文:
TextMessage
的正文是
String
。
BytesMessage
的正文是字节数组,等等。
在 JMS 1.1 中,使用消息类型特定的方法获取消息正文,例如,
TextMessage
上的
getText
方法。然而,在应用程序接收消息时,JMS API 总是将消息作为
javax.jms.Message
对象提供,该对象需要转换成相应的子类型才能获取正文。这不仅适用于从
receive
调用返回消息时,还适用于通过调用
MessageListener
的
onMessage
方法异步传递消息时。
例如,如果使用
receive
方法接收
TextMessage
,则需要将返回的对象从
Message
转换成
TextMessage
,然后调用
getText
方法:
Message message = consumer.receive(1000); // returns a TextMessage
String body = ((TextMessage) message).getText();
JMS 2.0 新增了一种方法,可以稍微简化提取消息正文的过程。这就是
Message
上的
getBody
方法,经典和简化的 API 用户均可使用。此方法接受预期的正文类型作为参数,无需对消息或正文进行转换:
Message message = consumer.receive(1000); // returns a TextMessage
String body = message.getBody(String.class);
我们来看看
getBody
如何简化获取其他消息类型的正文所需的代码。
如果消息为
BytesMessage
,JMS 1.1 提供了几种方式可从
BytesMessage
提取字节数组。最简单的方式是对
BytesMessage
调用
readBytes
方法。这会将字节复制到指定的字节数组。
以下是接收
BytesMessage
并以字节数组形式获取正文的
MessageListener
的示例:
void onMessage(Message message){ // delivers a BytesMessage
int bodyLength = ((BytesMessage)message).getBodyLength();
byte[] bytes = new byte[bodyLength];
int bytesCopied = ((BytesMessage)message).readBytes(bytes);
在 JMS 2.0 中,
getBody
方法可以大大简化这一过程:
void onMessage(Message message){ // delivers a BytesMessage
byte[] bytes = message.getBody(byte[].class);
如果消息为
ObjectMessage
,在 JMS 1.1 中,需要对
ObjectMessage
调用
getObject
方法,然后将返回的
Serializable
转换成希望的正文类型:
void onMessage(Message message){ // delivers an ObjectMessage
MyObject body = (MyObject)((ObjectMessage) message).getObject();
注意,需要执行两次转换。需要将消息从
Message
转换成
ObjectMessage
,以便调用
getObject
。这将以
Serializable
形式返回正文,然后需要将其转换成实际类型。
在 JMS 2.0 中,无需任何转换即可执行此操作:
void onMessage(Message message){ // delivers an ObjectMessage
MyObject body = message.getBody(MyObject.class);
最后,如果消息为
MapMessage
,可以通过
getBody
方法以
Map
形式返回正文:
Message message = consumer.receive(1000); // returns a MapMessage
Map body = message.getBody(Map.class);
StreamMessage
是一种不能使用
getBody
的消息类型。这是因为流通常由应用程序应单独读取的多个对象组成。
使用
getBody
方法时,如果指定的类与正文类型不符,将引发
MessageFormatException
。
Message
还新增了一个伴随方法
isBodyAssignableTo
,可用于测试对
getBody
的后续调用是否能够将特定
Message
对象的正文以特定类型的形式返回。如果希望将出现多种类型的消息,这将很有用。
直接接收消息正文的方法
在 JMS 1.1 中,同步使用消息的应用程序对
MessageConsumer
使用
receive()
、
receive(timeout)
或
receiveNoWait()
方法。
在 JMS 2.0 中,使用简化的 API 的应用程序可以对
JMSConsumer
使用同样的方法执行此操作。
这些方法返回一个
Message
对象,可以从中获取消息正文。之前介绍的
getBody
方法提供了一种从该对象获取正文的简单方式。
使用 JMS 2.0 简化的 API 的应用程序还有一个选择。
JMSConsumer
提供了三种方法 —
receiveBody(class)
、
receiveBody(class, timeout)
和
receiveBodyNoWait(class)
— 这三种方法将同步接收下一条消息并返回消息正文。与
getBody
一样,预期的类也是作为参数传递的。
因此无需使用清单 5 或清单 6 中的代码,应用程序使用清单 7 所示的一行代码即可。
JMSConsumer consumer = ...
Message message = consumer.receive(1000); // returns a TextMessage
String body = ((TextMessage) message).getText();
JMSConsumer consumer = ...
Message message = consumer.receive(1000); // returns a TextMessage
String body = message.getBody(String.class);
JMSConsumer consumer = ...
String body = consumer.receiveBody(String.class,1000);
清单 7
除了
StreamMessage
(原因同样是此消息类型不支持
getBody
)和
Message
(因为它没有正文),
receiveBody
方法可用于接收任意类型的消息,只要预先知道预期的正文类型。
只有
JMSConsumer
新增了这些方法。
MessageConsumer
没有新增这些方法。这意味着此特性只能用于使用 JMS 2.0 简化的 API 的应用程序。
而且,这些方法不提供对消息头或属性(如
JMSRedelivered
消息头字段或
JMSXDeliveryCount
消息属性)的访问,因此应只用于应用程序无需访问它们的情况。
创建会话的新方法
在 JMS 1.1 中,使用
javax.jms.Connection
上的以下方法创建
javax.jms.Session
,其中,transacted 参数需要设置为
true
或
false
,
acknowledgeMode
参数需要设置为
Session.AUTO_ACKNOWLEDGE
、
Session.CLIENT_ACKNOWLEDGE
或
Session.DUPS_OK_ACKNOWLEDGE
。
Session createSession(
boolean transacted, int acknowledgeMode) throws JMSException
这一直是一个相当令人困惑的方法,原因主要有二:
-
它使用两个参数定义会话的一个方面。
-
在 Java EE 事务中,这两个参数最终都会被忽略。
我们依次考虑这两个问题。
两个参数定义同一事物
JMS 1.1 中
createSession
方法的第一个问题是,它使用两个参数定义的实际上是会话的一个方面,有四种可能:
-
如果
transacted
参数设置为
false
,则会话是非事务性的,接收消息时应使用用于指定三种确认之一的
acknowledgeMode
参数。
-
如果
transacted
参数设置为
true
,则忽略
acknowledgeMode
参数。
除了不必要地使事情复杂化,这还导致代码潜在地具有误导性,因为如果
transacted
参数设置为
false
,用户仍需将
acknowledgeMode
参数设置为某个值,即使它会被忽略。例如,以下代码完全有效:
amb Session session =
connection.createSession(true,Session.AUTO_ACKNOWLEDGE);iguous
在 Java EE 事务中,这两个参数最终都会被忽略
JMS 1.1 中
createSession
方法中的第二个问题在 Java EE Web 或 EJB 应用程序中,如果当前有一个 JTA 事务(默认情况),
createSession
的两个参数最终都被忽略。但是,由于 API 强制开发人员指定两个参数,这导致代码非常容易产生误导,编写 EJB bean 的用户可能会写下以下代码,而没有意识到该会话实际上会使用 EJB 的容器托管的 JTA 事务。
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
为了解决这些问题,在
javax.jms.Connection
中新增了两个名为
createSession
的方法。一个有一个参数,另一个根本没有参数。我们将依次讨论这两个方法。
JMS 2.0:带一个参数的
createSession
在 JMS 2.0 中,为
javax.jms.Connection
添加了另一个
createSession
方法。这个方法有一个参数
sessionMode
:
Session createSession(int sessionMode) throws JMSException
在正常的 Java SE 环境中,
sessionMode
可设置为
Session.AUTO_ACKNOWLEDGE
、
Session.CLIENT_ACKNOWLEDGE
、
Session.DUPS_OK_ACKNOWLEDGE
或
Session.TRANSACTED
。在 Java EE 事务中,将忽略
sessionMode
。
JMS 2.0:不带参数的
createSession
在 Java EE 事务中,即使向
createSession
传递一个参数也有误导性,因为如果是 Java EE 事务,将忽略该参数。为了让用户能够编写误导性较小的代码,为
javax.jms.Connection
添加了第三个
createSession
方法,该方法没有参数:
Session createSession() throws JMSException
该方法特别适用于 Java EE 事务,其中指定会话模式没有意义,因为会被忽略。不过,此方法也可用于正常 Java SE 环境,在这种情况下,等同于调用
createSession(Session.AUTO_ACKNOWLEDGE)
。
JMS 2.0:带两个参数的
createSession
现有方法
createSession(boolean transacted,int acknowledgeMode)
仍然可以使用,将作为 API 的一部分永久保留。但是,鼓励开发人员使用此方法的一个参数或无参数版本。
JMS 2.0 中资源配置更轻松
JMS 2.0 通过多种方式简化了资源配置。
Java EE 中的默认连接工厂
Java EE 7 引入了一个
平台默认 JMS 连接工厂
。这是内置的连接工厂,连接到应用服务器的内置 JMS 提供程序。
应用程序可以通过使用名称
java:comp:DefaultJMSConnectionFactory
执行 JNDI 查询获取此连接工厂,无需先使用管理工具创建连接工厂:
@Resource(lookup="java:comp/DefaultJMSConnectionFactory") ConnectionFactory cf
此连接工厂旨在用于许多使用内置 JMS 提供程序的应用程序,无需添加任何应用程序特定的配置。
将
JMSContext
注入应用程序时,使用
JMSConnectionFactory
批注指定要使用的连接工厂:
@Inject @JMSConnectionFactory("
jms/connectionFactory") JMSContext context1;
如果省略此批注,将使用默认连接工厂:
@Inject JMSContext context2; // uses the platform default connection factory
Java EE 中的 JMS 资源定义批注
每个 JMS 应用程序一开始都具有一个连接工厂(实现
javax.jms.ConnectionFactory
的对象)和至少一个目标(实现
javax.jms.Queue
或
javax.jms.Topic
的对象)。
ConnectionFactory
是 JMS 中用于创建到 JMS 提供程序的连接的对象,
Queue
或
Topic
是标识发送或接收消息的物理队列或主题的对象。
不同 JMS 提供程序创建和配置这些对象的方式各不相同。因此,JMS 建议您使用单独的、提供程序特定的工具来创建、配置应用程序需要的连接工厂和目标并将其绑定到 JNDI 存储中。然后应用程序可以使用 JNDI 查询这些对象,无需使用任何非标准模式。除了保持应用程序代码的可移植性,这还意味着无需了解代码部署方式的详细信息,即可编写代码。
配置
ConnectionFactory
时,通常需要知道 JMS 服务器的主机名和端口等内容。配置
Queue
或
Topic
对象时,通常需要知道队列或主题的物理名称。从应用程序分别创建
ConnectionFactory
、
Queue
和
Topic
对象并将其存储在 JNDI 中,让部署人员或管理员而不是开发人员来定义这些详细信息。
尽管在许多企业环境中分离代码与配置非常重要,但在较简单的环境中,这可能是一项不希望出现的负担。此外,如果将应用程序部署到一个自动化的平台即服务 (PaaS) 系统中,可能需要自动化供应应用程序所需的
ConnectionFactory
、
Queue
和
Topic
对象。
在许多 Java EE 应用程序中,现在任何 Java EE 7 应用服务器中均有默认 JMS 连接工厂(上节介绍过),因此根本无需配置任何连接工厂。但对于需要专门配置连接工厂的情况(以及队列和主题),Java EE 7 提供的另一个新特性允许使用代码中的批注、部署描述文件中的 XML 元素或二者组合来创建这些对象。
主要的新批注为
javax.jms.JMSConnectionFactoryDefinition
和
javax.jms.JMSDestinationDefinition
。这些可以在 EJB bean 或 servlet 等任何 Java EE 组件类中定义,如清单 8 所示:
@JMSConnectionFactoryDefinition(
name="java:global/jms/MyConnectionFactory",
maxPoolSize = 30,
minPoolSize= 20,
properties = {
"addressList=mq://localhost:7676",
"reconnectEnabled=true"
@JMSDestinationDefinition(
name = "java:global/jms/DemoQueue",
interfaceName = "javax.jms.Queue",
destinationName = "demoQueue"
public class NewServlet extends HttpServlet {
清单 8
如果需要定义多个连接工厂或目标,需要将这些批注包含在
JMSConnectionFactoryDefinitions
或
JMSDestinationDefinitions
批注中,如清单 9 所示:
@JMSConnectionFactoryDefinitions({
@JMSConnectionFactoryDefinition(
name="java:global/jms/MyConnectionFactory1",
maxPoolSize = 30,
minPoolSize= 20,
properties = {
"addressList=mq://localhost:7676",
"reconnectEnabled=true"
@JMSConnectionFactoryDefinition(
name="java:global/jms/MyConnectionFactory2",
maxPoolSize = 30,
minPoolSize= 20,
properties = {
"addressList=mq://localhost:7677",
"reconnectEnabled=true"
@JMSDestinationDefinitions({
@JMSDestinationDefinition(
name="java:global/jms/DemoQueue1",
interfaceName = "javax.jms.Queue",
destinationName = "demoQueue1"
@JMSDestinationDefinition(
name="java:global/jms/DemoQueue2",
interfaceName = "javax.jms.Queue",
destinationName = "demoQueue2"
public class NewServlet extends HttpServlet {
清单 9
JMSConnectionFactoryDefinition
批注定义一些可以指定的标准属性,包括
name
(即 JNDI 名称)、
clientId
、
user
、
password
、
maxPoolSize
和
minPoolSize
。此外,还可以使用
properties
属性指定应用服务器可能支持的其他非标准属性。清单 8 和清单 9 中的
addressList
和
reconnectEnabled
就是这些非标准属性的示例。
JMSConnectionFactoryDefinition
批注定义较少的可以指定的标准属性,包括
name
(即 JNDI 名称)和
destinationName
(即提供程序特定的队列或主题名称),并允许使用 properties 属性指定其他非标准属性。
用这种方式定义的连接工厂和目标必须位于
java:comp
、
java:module
、
java:app
或
java:global
命名空间中,其存在时间通常与定义它们的应用程序的部署时间一样长。
还可以在部署描述文件(如
web.xml
或
ejb-jar.xml
)中指定这些定义,如清单 10 所示:
<jms-connection-factory>
<name>java:global/jms/MyConnectionFactory</name>
<max-pool-size>30</max-pool-size>
<min-pool-size>20</min-pool-size>
<property>
<name>addressList</name>
<value>mq://localhost:7676</value>
</property>
<property>
<name>reconnectEnabled</name>
<value>true</value>
</property>
</jms-connection-factory>
<jms-destination>
<name>java:global/jms/DemoQueue</name>
<interfaceName>javax.jms.Queue</interfaceName>
<destinationName>demoQueue</destinationName>
</jms-destination>
清单 10
如果需要,开发人员可以在批注中指定某些必需的属性,由部署人员在部署描述文件中指定其余属性。对于到部署时才知道值的属性,这可能非常有用。
在以上所有示例中,应用服务器负责“供应”批注或部署描述文件中定义的 JNDI 资源。不过,部署人员仍需负责确保连接工厂所指向的 JMS 服务器已经安装并且可用,且已经创建物理队列和主题本身。
总结
在本文中,我们介绍了 JMS 2.0 中新增的易用性特性,该特性可显著减少开发人员需要编写的代码。在第 2 部分中,我们将介绍 JMS 2.0 中新增的消息传递特性。
另请参见
关于作者
Nigel Deakin 是 Oracle 的技术人员中的一位主要成员,他是 JSR 343 (Java Message Service 2.0) 规范的带头人。除了负责领导 JMS 规范的后续版本,他还是 Oracle JMS 开发团队的成员,负责 Open Message Queue 和 GlassFish 应用服务器。他最近在美国旧金山的 JavaOne 大会、比利时安特卫普的 Devoxx 大会上发表了演讲,目前他在英国剑桥工作。
分享交流
请在
Facebook
、
Twitter
和
Oracle Java 博客
上加入 Java 社区对话!
Integrated Cloud Applications & Platform Services