Java EE 7最近已被执行委员会接受 。 这意味着我们很快将在市场上提供Java EE 7应用服务器。 构成Java EE 7的规范之一是JMS 2.0 。 自1.1版以来,引入了一些有趣的改进。
JMS有很多怪异的东西,例如: Connection#createSession(boolean Transacted,int acceptMode)方法。
第一个方法参数( transacted
)定义是否应处理会话。 Java EE应用程序具有负责事务处理的JTA。 我们可以选择是否支持交易(默认)。 那么,为什么我们需要这个论点呢?
第二个方法参数( acknowledgeMode
)是一个从Session
对象获取的整数常量 。 认真地说,整数常量使该API看起来太过传统。 最后,Java EE环境中这些参数的含义是什么? JavaDoc的说, acknowledgeMode
如果会话事务被忽略。
这是什么意思? 不外乎: 这些参数对于Java EE上下文中使用的JMS生产者没有任何意义 。 这就是为什么他们鼓励您使用(true, 0)
参数值以避免不必要的混淆的原因。 这闻起来有遗味。
好的,让我们回到主要主题。 我想看一下JMS世界中的新功能,以及它如何使我能够更轻松,更可维护的方式进行编码。 我已经准备了一个使用JMS 2.0,JAX-RS和EJB(SLSB和MDB)的简单Web应用程序,并将其推送到这里的 github存储库中。
先决条件和基础架构
为了能够运行此代码,您应该创建一个示例队列。 我已经在jms/queue/myqueue
JNDI名称下配置了它。
我正在使用Glassfish v4 build 87 。 要使用Java EE 7 API,我需要添加以下Maven依赖项:
<dependency><groupId>javax</groupId><artifactId>javaee-api</artifactId><version>7.0-b87</version><scope>provided</scope>
</dependency>
驻留在此存储库中:
<repository><id>Java EE 7</id><url>https://maven.java.net/content/groups/promoted/</url>
</repository>
这就是配置的全部内容。
有效负载和REST配置
BusinessObject
是一个简单的对象,将充当我们的JMS消息的有效负载。 它将由生产者发送并由消费者接收。 这里没什么好想的,所以让我们继续前进。 RESTConfiguration
甚至更简单–它只是为我们的应用程序定义了JAX-RS端点前缀。 该前缀是:“ / rest”。 您可以通过访问应用程序URL / rest / producer / jms11或/ rest / producer / jms20(例如http://localhost:8080/jms2_0_spike/rest/producer/jms20
)来调用生产者EJB。
JMS生产者
现在,这里开始变得有趣。 在下面,您可以找到SLSB JMS消息产生器的JMS 1.1代码:
@Stateless
public class JMS11Producer {@Resource(lookup = "jms/__defaultConnectionFactory")private ConnectionFactory connectionFactory;@Resource(lookup = "jms/queue/myqueue")private Queue queue;@Path("/jms11")@GETpublic String produce() {String status = "OK";Connection connection = null;try {connection = connectionFactory.createConnection();Session session = connection.createSession(true, 0);MessageProducer producer = session.createProducer(queue);BusinessObject payload = new BusinessObject(UUID.randomUUID().toString());ObjectMessage message = session.createObjectMessage();message.setObject(payload);producer.send(message);} catch (JMSException e) {status = e.getMessage();} finally {if (connection != null) {try {connection.close();} catch (JMSException e) {status = e.getMessage();}}}return status;}
}
尽管JAX-RS结果状态出现问题,但这是一个令人费解的样板代码,模糊了方法的主要职责。 它应该只向队列发送一条消息,但是它却做了很多事情。 它会创建一个连接,会话(包括讨厌的,忽略的参数在内),对象类型的消息,将其初始化,然后最终将消息发送到队列中……哦,是的–当然,不要忘记检查异常和嵌套的try / catch块。 我们可以尝试去优化它-通过创建连接移动到一些如@PostConstruct
方法和关闭,以@PreDestroy
-但它仍然有很多unnecesary噪音。
现在,让我们看一下功能上与JMS 2.0中表达的代码相同的代码:
@Stateless
public class JMS20Producer {@Resource(lookup = "jms/queue/myqueue")private Queue queue;@Injectprivate JMSContext jmsContext;@Path("/jms20")@GETpublic String produce() {BusinessObject payload = new BusinessObject(UUID.randomUUID().toString());jmsContext.createProducer().send(queue, payload);return "OK";}
}
很整洁吧? 弄清楚此方法的作用要容易得多:它创建有效负载并将其发送到队列。 就这样–这就是方法的全部内容。 异常处理,连接和会话创建,消息类型–一切都为我们完成。 如果可以将这些职责转移到容器上以减轻开发人员的生活负担,那么为什么不这样做呢?
让我们看一下此示例中使用的一些JMS 2.0功能:
- 不需要
ConnectionFactory
, - 无需
Connection
或Session
, -
JMSContext
是结合了Connection
和Session
功能的新对象。 它可以通过容器注入, - 没有检查的异常–仅
JMSRuntimeException
, - 用于消息生成的链调用使其更易于阅读。
JMS消费者
自JMS 1.1以来,Message Consumer
并未发生太大变化。 它仍然是MDB,但是现在由于使用message.getBody(Clazz)
进行消息投射,更容易达到预期的有效负载:
@MessageDriven(mappedName = "jms/queue/myqueue")
public class Consumer implements MessageListener {@Overridepublic void onMessage(Message message) {try {// In JMS 1.1:// ObjectMessage objectMessage = (ObjectMessage)message;// BusinessObject payload = (BusinessObject)objectMessage.getObject();BusinessObject payload = message.getBody(BusinessObject.class);System.out.println("Message received: " + payload);} catch (JMSException e) {System.err.println("Error while fetching message payload: " + e.getMessage());}}
}
结论
这只是对JMS 2.0的快速浏览。 但是,我发现有趣的是,与JMS 1.1相比,可以产生多少更清晰的代码。 有关JMS 2.0的更多详细信息,请查看其正式规范 。
翻译自: https://www.javacodegeeks.com/2013/05/java-ee-7-jms-2-0-with-glassfish-v4.html