开发 Web 应用与 Java 消息服务
1. 转换器和验证器
在 Java 开发中,托管 Bean 可以分配转换器和验证器。Java 类有强类型字段,但数据到达网页时需转换为字符串。JSF 会自动为 Java 数据类型执行此转换,不过也能编写自定义转换器并添加特定应用逻辑。
1.1 验证器的使用
验证器可对托管 Bean 中的数据设置约束。可以使用预定义注解(如
@NotNull
、
@Min
和
@Max
)来指定 Bean 字段的数据验证约束,也能创建自定义注解实现特定应用的约束。以下是
Student
类的示例:
@ManagedBean
@RequestScoped
public class Student implements Serializable{
@NotNull(message="you must enter Student ID")
@Size(min=8, max=10, message="Student ID must include between 8 and 10 symbols")
private long studentId;
@Size(min=2, message="Name can have less than two characters")
private String name;
@NotNull(message="Please enter Class ID")
private int classId;
...
}
核心 JSF 库有标准验证器,可验证长度以及长整型和双精度型值的范围。可以在 JSF 文件中封装这些验证器,示例如下:
<h:inputText value="#{student.name}" title="name" id="name" >
<f:validateLength minimum="2"/>
</h:inputText>
1.2 自定义验证器
要创建自定义验证器(如
CreditCardValidator
),需编写一个实现
javax.faces.validator.Validator
接口的 Java 类,并在
validate()
方法中执行验证。若值无效,该方法必须抛出
ValidatorException
。自定义验证器必须在
faces-config.xml
中注册:
<validator>
<validator-id>CreditCardValidator</validator-id>
<validator-class>myvalidators.CreditCardValidator</validator-class>
</validator>
自定义验证器可在 JSF 页面中使用
<f: validator>
标签,示例如下:
<h:inputText value="#{student.creditcad}" title="card" id="card" >
<f:validator id="CreditCardValidator"/>
</h:inputText>
2. 使用 JSF 开发股票服务器应用
2.1 需求概述
重写之前创建的股票服务器应用,这次使用 JSF。JSF 客户端应有一个文本输入框和一个提交按钮。用户输入股票代码获取价格报价,生成随机报价并返回包含报价的网页。网页应显示可用股票列表。托管 Bean 需有一个
getQuotes()
方法,用户点击提交按钮时调用。
2.2 实现步骤
-
创建新的
StockServerBean :包含getQuote()和getNasdaqSymbols()方法。 - 定义 JSF 模板 。
-
创建两个 JSF 页面
:一个用于请求特定股票的价格报价,另一个用于显示结果。第一个网页的提交按钮应调用托管 Bean 上的
getQuote()方法。 - 运行并测试 JSF 应用 。
2.3 开发提示
NetBeans 7 IDE 对 JSF 的支持优于 Eclipse,它有许多向导,尤其能在图形界面中轻松定义网页导航。另一个流行的 IDE IntelliJ IDEA 9 也对 JSF 提供了出色支持。
3. 介绍 JMS 和 MOM
3.1 消息传递概念
人们通过电子邮件、即时消息、Twitter、Facebook 等方式相互发送消息,也可使用传统方式如邮寄。应用程序可以使用消息导向中间件(MOM)相互发送消息,MOM 类似于快递服务。程序使用 Java 消息服务(JMS)API 将消息放入消息队列,消息会被传递到从该队列读取消息的另一个应用程序。
3.2 消息传递与传统通信的区别
之前学习了分布式 Java 应用中的几种数据交换方法(直接套接字通信、RMI 和基于 HTTP 的交互),但它们都基于远程过程调用(RPC)或请求/响应模型。MOM 能构建异步通信的分布式系统。JMS 是与 MOM 一起工作的 API,它本身不是消息传输方式,就像 JDBC 对于关系型数据库管理系统一样,Java 应用程序可以使用相同的 JMS 类与任何 MOM 提供商配合使用。
3.3 常见的 MOM 软件
| MOM 软件 | 提供商 |
|---|---|
| WebSphere MQ | IBM |
| EMS | Tibco Software |
| SonicMQ | Progress Software |
| ActiveMQ | 开源,Apache |
| Open MQ | 开源,Oracle |
3.4 同步与异步通信
同步通信(如调用远程机器上的
placeOrder()
方法)是阻塞调用,调用程序需等待方法执行完成或抛出错误才能继续。而异步通信中,可下单但无需等待执行,就像寄信或发邮件一样,发送后可继续做其他事情,收件人也无需在线接收。
3.5 交易订单处理流程
下单过程是将描述订单的 Java 对象放入 MOM 提供商的特定消息队列。下单后,程序可继续执行,无需等待订单处理完成。多个用户可将订单放入同一队列,另一个程序负责出队和处理消息。为提高基于消息的应用程序的吞吐量,可添加多个消费者从同一队列读取消息。
4. JMS 消息传递模式
4.1 消息传递的两种模式
4.1.1 点对点(P2P)消息传递
程序将消息发送到特定队列,另一个程序从该队列接收消息,消息成功接收后即从队列中删除。
4.1.2 发布/订阅(pub/sub)模式
程序发布消息供多个接收者消费,消息发布到特定主题,许多订阅者可订阅接收。主题代表应用程序和/或用户的重要消息,如价格下跌警报、突发新闻等。在 pub/sub 模式中,消息通常在所有订阅者接收后从队列中删除。聊天室就是 pub/sub 应用的一个很好例子。
4.2 消息传递的保证模式
消息传递可以是有保证的,MOM 会像邮局一样将消息保留在队列中,直到接收者获取。在这种模式下,消息是持久的,MOM 提供商将其存储在内部存储中(可以是数据库管理系统或文件系统)。在无保证模式下,MOM 仅将消息传递给消息到达时正在运行的接收应用程序。
5. JMS API 概述
5.1 主要 JMS 接口
| 接口名称 | 描述 |
|---|---|
| Queue | 消息存放或获取的地方,消息按先进先出(FIFO)规则检索。消息生产者将消息放入队列,消息消费者从队列中取出消息。 |
| QueueConnection |
表示与 MOM 的特定连接,类似于 JDBC 中的
Connection
类。
|
| QueueConnectionFactory |
创建
Connection
对象,类似于 JDBC 中的
DataSource
类。
|
| QueueSession |
表示客户端与 MOM 服务器之间的特定会话,由
QueueConnection
创建。
|
| QueueSender | 实际发送消息的对象。 |
| QueueReceiver | 接收消息的对象。 |
| TopicPublisher |
发布消息,功能类似于
QueueSender
。
|
| TopicSubscriber |
接收消息,功能类似于
QueueReceiver
。
|
| Topic |
在 pub/sub 模式中表示重要应用事件的对象,
TopicPublisher
向主题发布消息,
TopicSubscribers
可订阅该主题。
|
| Message | 作为应用程序特定对象的包装器,可放入 JMS 队列或发布到主题。 |
5.2 消息类型
每个消息包含头部、可选的主体和提供附加属性的功能。头部包含消息标识(唯一消息 ID、目的地、类型等),可选属性可由程序设置,用于标记具有特定应用数据的消息。可选主体包含要传递的消息,JMS 消息有以下几种类型:
-
TextMessage
:可包含任何 Java 字符串。
-
ObjectMessage
:可保存任何可序列化的 Java 对象。
-
BytesMessage
:保存字节数组。
-
StreamMessage
:包含 Java 基本类型的流。
-
MapMessage
:包含任何键/值对,例如
id=123
。
5.3 发送消息的步骤
-
创建(或从命名服务器获取)
ConnectionFactory对象。 -
创建
Connection对象并调用其start()方法。 -
创建
Session对象。 -
创建
Queue对象。 -
创建
MessageProducer对象。 -
创建消息对象(如
TextMessage)并放入数据。 -
调用
QueueSender的send()方法。 -
关闭
QueueSender、Session和Connection对象以释放系统资源。
以下是发送消息到名为
TestQueue
的队列的代码示例:
Session session=null;
ConnectionFactory factory;
QueueConnection connection=null;
try{
// 特定于 Open MQ 的连接创建方式
factory = new com.sun.messaging.ConnectionFactory();
factory.setProperty(ConnectionConfiguration.imqAddressList,
"mq://localhost:7677,mq://localhost:7677");
connection = factory.createQueueConnection("admin","admin");
connection.start();
session = connection.createQueueSession(
false, Session.AUTO_ACKNOWLEDGE);
Queue ioQueue = session.createQueue("TestQueue");
MessageProducer queueSender = session.createProducer(ioQueue);
// 以市场价格购买 200 股 IBM 股票
TextMessage outMsg = session.createTextMessage("IBM 200 Mkt");
queueSender.send(outMsg);
queueSender.close();
System.out.println(
"Successfully placed an order to purchase 200 shares of IBM");
}
catch (JMSException e){
System.out.println("Error: " + e.getMessage());
}
finally{
try{
session.close();
connection.close();
} catch (Exception e) {
System.out.println("Can’t close JMS connection/session " +
e.getMessage());
}
}
5.4 接收消息的方式
接收消息的程序称为消息消费者,可以是独立的 Java 程序或消息驱动 Bean(MDB)。可以使用
receive()
方法同步接收消息,也可以通过实现
MessageListener
接口并编程回调
onMessage()
方法异步接收消息。
5.4.1 同步接收消息
QueueReceiver queueReceiver = Session.createReceiver(ioQueue);
Message myMessage = queueReceiver.receive();
// 设置超时时间为 500 毫秒
Message myMessage = queueReceiver.receive(500);
5.4.2 异步接收消息
异步接收消息是更好的方式,因为消息消费者无需多次向 MOM 发送请求来检查消息是否在队列中。当消息放入队列时,
onMessage()
回调方法将立即被调用。以下是异步接收消息的示例代码:
package com.practicaljava.lesson30;
import javax.jms.*;
import com.sun.messaging.ConnectionFactory;
import com.sun.messaging.ConnectionConfiguration;
public class MessageReceiver implements MessageListener{
Session session=null;
ConnectionFactory factory;
QueueConnection connection=null;
MessageConsumer consumer=null;
MessageReceiver(){
try{
factory = new com.sun.messaging.ConnectionFactory();
factory.setProperty(ConnectionConfiguration.imqAddressList,
"mq://localhost:7677,mq://localhost:7677");
connection = factory.createQueueConnection("admin","admin");
connection.start();
Session session = connection.createQueueSession(
false, Session.AUTO_ACKNOWLEDGE);
Queue ioQueue = session.createQueue( "TestQueue" );
consumer = session.createConsumer(ioQueue);
consumer.setMessageListener(this);
System.out.println("Listening to the TestQueue...");
// 等待消息
Thread.sleep(100000);
} catch (InterruptedException e){
System.out.println("Error: " + e.getMessage());
}
catch (JMSException e){
System.out.println("Error: " + e.getMessage());
}
finally{
try{
connection.close();
} catch (Exception e) {
System.out.println("Can’t close JMS connection/session "
+ e.getMessage());
}
}
}
public static void main(String[] args){
new MessageReceiver();
}
public void onMessage(Message msg){
String msgText;
try{
if (msg instanceof TextMessage){
msgText = ((TextMessage) msg).getText();
System.out.println("Got from the queue: " + msgText);
}else{
System.out.println("Got a non-text message");
}
}
catch (JMSException e){
System.out.println("Error while consuming a message: " +
e.getMessage());
}
}
}
5.5 消息确认模式
消息确认模式在创建
Session
对象时定义。
createSession()
方法有两个参数,如果第一个参数为
true
,会话是事务性的,第二个参数的值无关紧要,消费者可以提交或回滚消息。如果调用了
commit()
方法,消息将从队列中删除;
rollback()
方法会将消息保留在队列中。如果会话是非事务性的,第二个参数定义确认模式:
-
AUTO_ACKNOWLEDGE
模式:
onMessage()
方法成功完成后立即发送确认。
-
CLIENT_ACKNOWLEDGE
模式:需要显式确认,如
msg.acknowledge()
,这允许从队列中删除消息。
-
DUP_OK_ACKNOWLEDGE
模式:用于服务器故障的情况,同一消息可能会多次传递,在某些用例中是可以接受的。
总结
本文介绍了 JSF 开发中的转换器和验证器,包括自定义验证器的创建和使用。同时详细阐述了 JMS 和 MOM 的概念、消息传递模式、JMS API 以及消息的发送和接收方式,还介绍了消息确认模式。通过这些知识,可以更好地开发基于消息传递的分布式 Java 应用程序。
6. 消息传递架构中的 Java EE 应用服务器
在消息传递架构中引入 Java EE 应用服务器具有诸多好处。Java EE 应用服务器可以提供更强大的管理和监控功能,确保消息传递的可靠性和性能。它可以管理连接池、事务处理和资源分配,使得消息传递系统更加稳定和高效。
6.1 连接管理
Java EE 应用服务器可以管理 JMS 连接,避免手动创建和关闭连接带来的资源浪费和错误。通过连接池技术,服务器可以重用连接,提高系统的响应速度。例如,在高并发的情况下,连接池可以快速提供可用的连接,减少连接创建的时间开销。
6.2 事务处理
在 Java EE 应用服务器中,可以使用 JTA(Java Transaction API)来管理消息传递的事务。事务处理可以确保消息的一致性和完整性。例如,在一个业务流程中,如果消息的发送和数据库的更新是一个原子操作,那么可以使用事务来保证要么两者都成功,要么两者都失败。
6.3 监控和管理
Java EE 应用服务器提供了丰富的监控和管理工具,可以实时监控消息队列的状态、消息的处理情况和资源的使用情况。管理员可以通过这些工具及时发现和解决问题,确保系统的正常运行。
7. 消息驱动 Bean 的价值
消息驱动 Bean(MDB)是 Java EE 中的一种特殊的企业 Bean,它可以异步处理 JMS 消息。MDB 为消息传递架构带来了很多价值。
7.1 异步处理
MDB 可以异步处理消息,使得应用程序可以在不阻塞主线程的情况下处理消息。这对于提高系统的吞吐量和响应速度非常有帮助。例如,在一个电商系统中,当用户下单后,系统可以将订单消息发送到消息队列,MDB 可以异步处理这些订单,而主线程可以继续处理其他请求。
7.2 解耦
MDB 可以将消息的发送者和接收者解耦。发送者只需要将消息发送到消息队列,而不需要关心谁来处理这些消息。接收者只需要从消息队列中接收消息并处理,而不需要关心消息是从哪里来的。这种解耦可以提高系统的灵活性和可维护性。
7.3 可扩展性
MDB 可以很容易地进行扩展。可以通过增加 MDB 的实例数量来提高消息处理的能力。例如,在高并发的情况下,可以增加 MDB 的实例数量,使得系统可以同时处理更多的消息。
8. 实际应用案例
以下是一个简单的实际应用案例,展示如何使用 JMS 和 MDB 来构建一个分布式系统。
8.1 系统架构
graph LR
A[客户端] -->|发送消息| B(消息队列)
B -->|接收消息| C(MDB)
C -->|处理消息| D(数据库)
8.2 代码实现
8.2.1 消息发送端
import javax.jms.*;
import com.sun.messaging.ConnectionFactory;
import com.sun.messaging.ConnectionConfiguration;
public class MessageSender {
public static void main(String[] args) {
Session session = null;
ConnectionFactory factory;
QueueConnection connection = null;
try {
factory = new com.sun.messaging.ConnectionFactory();
factory.setProperty(ConnectionConfiguration.imqAddressList,
"mq://localhost:7677,mq://localhost:7677");
connection = factory.createQueueConnection("admin", "admin");
connection.start();
session = connection.createQueueSession(
false, Session.AUTO_ACKNOWLEDGE);
Queue ioQueue = session.createQueue("OrderQueue");
MessageProducer queueSender = session.createProducer(ioQueue);
TextMessage outMsg = session.createTextMessage("Order: 123");
queueSender.send(outMsg);
queueSender.close();
System.out.println("Successfully sent an order message");
} catch (JMSException e) {
System.out.println("Error: " + e.getMessage());
} finally {
try {
session.close();
connection.close();
} catch (Exception e) {
System.out.println("Can’t close JMS connection/session " +
e.getMessage());
}
}
}
}
8.2.2 消息驱动 Bean
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "OrderQueue")
})
public class OrderMDB implements MessageListener {
@Override
public void onMessage(Message message) {
try {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
String order = textMessage.getText();
System.out.println("Received order: " + order);
// 处理订单,例如将订单信息存入数据库
} else {
System.out.println("Got a non-text message");
}
} catch (JMSException e) {
System.out.println("Error while consuming a message: " +
e.getMessage());
}
}
}
8.3 案例分析
在这个案例中,客户端将订单消息发送到消息队列,MDB 从消息队列中接收消息并处理订单。通过这种方式,客户端和 MDB 实现了解耦,系统的可扩展性和灵活性得到了提高。同时,使用 Java EE 应用服务器可以管理连接和事务,确保消息传递的可靠性。
9. 总结与展望
9.1 总结
本文全面介绍了 JSF 开发中的转换器和验证器,为 Web 应用的数据处理提供了有效的手段。同时,深入探讨了 JMS 和 MOM 的相关知识,包括消息传递模式、JMS API、消息的发送和接收方式以及消息确认模式。此外,还介绍了 Java EE 应用服务器在消息传递架构中的作用和消息驱动 Bean 的价值,并通过实际案例展示了如何构建基于消息传递的分布式 Java 应用程序。
9.2 展望
随着技术的不断发展,消息传递在分布式系统中的应用将越来越广泛。未来,我们可以期待更高效、更可靠的消息传递技术的出现。例如,随着云计算和容器化技术的发展,消息传递系统可以更加灵活地部署和扩展。同时,人工智能和机器学习技术也可以应用于消息传递系统,实现智能的消息路由和处理。在开发过程中,我们需要不断学习和掌握新的技术,以适应不断变化的需求。
通过对这些知识的掌握和应用,开发者可以构建出更加稳定、高效和灵活的分布式 Java 应用程序,满足不同业务场景的需求。在实际项目中,需要根据具体的需求选择合适的技术和架构,充分发挥消息传递的优势,提高系统的性能和可靠性。
超级会员免费看

被折叠的 条评论
为什么被折叠?



