上一篇文章講了實體bean,瞭解了實體Bean的在工作流程中的使用,這篇文章我們來看一下消息驅動bean的使用。我們先來了解一下JMS.
JMS是Java的消息服務,JMS的客戶端之間可以通過JMS服務進行異步的消息傳輸。JMS支持兩種消息模型:Point-to-Point(P2P)和Publish/Subscribe(Pub/Sub),即點對點和發佈訂閱模型。
P2P
在P2P模型中,有下列概念:消息隊列(Queue)、發送者(Sender)、接收者(Receiver)。每個消息都被髮送到一個特定的隊列,接收者從隊列中獲取消息。隊列保留着消息,直到它們被消費或超時。
l 每個消息只有一個消費者(Consumer)(即一旦被消費,消息就不再在消息隊列中)
l 發送者和接收者之間在時間上沒有依賴性,也就是說當發送者發送了消息之後,不管接收者有沒有正在運行,它不會影響到消息被髮送到隊列。
l 接收者在成功接收消息之後需向隊列應答成功
如果你希望發送的每個消息都應該被成功處理的話,那麼你需要P2P模型。
Pub/Sub
在Pub/Sub模型中,有下列概念: 主題(Topic)、發佈者(Publisher)、訂閱者(Subscriber)。客戶端將消息發送到主題。多個發佈者將消息發送到Topic,系統將這些消息傳遞給多個訂閱者。
l 每個消息可以有多個消費者
l 發佈者和訂閱者之間有時間上的依賴性。針對某個主題(Topic)的訂閱者,它必須創建一個訂閱之後,才能消費發佈者的消息,而且,爲了消費消息,訂閱者必須保持運行的狀態。
當然,爲了緩和這種嚴格的時間相關性,JMS允許訂閱者創建一個可持久化的訂閱。這樣,即使訂閱者沒有被激活(運行),它也能接收到發佈者的消息。
如果你希望發送的消息可以不被做任何處理、或者被一個消費者處理、或者可以被多個消費者處理的話,那麼可以採用Pub/Sub模型。
消息的消費
在JMS中,消息的產生和消費是異步的。對於消費來說,JMS的消費者可以通過兩種方式來消費消息。
l 同步 – 訂閱者或接收者調用receive方法來接收消息,receive方法在能夠接收到消息之前(或超時之前)將一直阻塞。
l 異步 – 訂閱者或接收者可以註冊爲一個消息監聽器。當消息到達之後,系統自動調用監聽器的onMessage方法。
JMS編程模型
Connection Factory
創建Connection對象的工廠,針對兩種不同的JMS消息模型,分別有QueueConnectionFactory和TopicConnectionFactory兩種。可以通過JNDI來查找ConnectionFactory對象。
Destination
Destination的意思是消息生產者的消息發送目標或者說消息消費者的消息來源。對於消息生產者來說,它的Destination是某個隊列(Queue)或某個主題(Topic);對於消息消費者來說,它的Destination也是某個隊列或主題(即消息來源)。
所以,Destination實際上就是兩種類型的對象:Queue、Topic。可以通過JNDI來查找Destination。
Connection
Connection表示在客戶端和JMS系統之間建立的鏈接(對TCP/IPsocket的包裝)。Connection可以產生一個或多個Session。跟ConnectionFactory一樣,Connection也有兩種類型:QueueConnection和TopicConnection。
Session
Session是我們操作消息的接口。可以通過session創建生產者、消費者、消息等。Session提供了事務的功能。當我們需要使用session發送/接收多個消息時,可以將這些發送/接收動作放到一個事務中。同樣,也分QueueSession和TopicSession。
消息生產者
消息生產者由Session創建,並用於將消息發送到Destination。同樣,消息生產者分兩種類型:QueueSender和TopicPublisher。可以調用消息生產者的方法(send或publish方法)發送消息!
消息消費者
消息消費者由Session創建,用於接收被髮送到Destination的消息。兩種類型:QueueReceiver和TopicSubscriber。可分別通過session的createReceiver(Queue)或createSubscriber(Topic)來創建。當然,也可以通過session的createDurableSubscriber方法來創建持久化的訂閱者。
MessageListener
消息監聽器。如果註冊了消息監聽器,一旦消息到達,將自動調用監聽器的onMessage方法。EJB中的MDB(Message-DrivenBean)就是一種MessageListener。
對客戶端來說,message-drivenbean(消息驅動bean)就是異步消息的消費者。當消息到達之後,由容器負責調用MDB。客戶端發送消息到destination,MDB作爲一個MessageListener接收消息。
下邊我們通過一個例子來了解一下消息驅動bean的使用。
我們建立一個ejb項目,然後建立一個SmsSendMessage類,並實現MessageListener接口,並實現方法onMessage方法,當有消息發送到容器後,容器會自動的調用onMessage方法處理消息。
package com.tgb.message;
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 = "queue/myqneue")
})
public class SmsSendMessage implements MessageListener {
//當有消息的時候,容器會自動調用onMessage方法。
@Override
public void onMessage(Message message) {
try {
TextMessage textMessage = (TextMessage)message;
System.out.println("MyMDBBean被調用了!【"+textMessage.getText()+"】");
} catch (JMSException e) {
e.printStackTrace();
}
}
}
然後我們在建立一個客戶端,用來發送消息,代碼如下,
package com.tgb.jms;
import java.util.Properties;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JmsClient {
public static void main(String[] args) {
Properties props = new Properties();
props.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
try {
// 建立上下文
InitialContext ctx = new InitialContext(props);
// 獲取ConnectionFactory對象
QueueConnectionFactory factory = (QueueConnectionFactory) ctx
.lookup("ConnectionFactory");
// 創建QueueConnection對象
QueueConnection connection = factory.createQueueConnection();
// 創建QueueSession對象,第一個參數表示事務自動提交,第二個參數標識一旦消息被正確送達,將自動發回響應
QueueSession session = connection.createQueueSession(false,
QueueSession.AUTO_ACKNOWLEDGE);
// 獲得Destination對象
//這裏的queue/myqneue 就是我們在消息處理類的註解中的Destination的值 ( 此處這樣寫,如果不做其他的配置,可能會有問題)
Queue queue = (Queue) ctx.lookup("queue/myqneue");
// 創建文本消息
TextMessage msg = session.createTextMessage("世界,你好");
// 創建發送者
QueueSender sender = session.createSender(queue);
// 發送消息
sender.send(msg);
// 關閉會話
session.close();
} catch (NamingException e) {
System.out.println(e.toString());
e.printStackTrace();
} catch (JMSException e) {
// TODO Auto-generated catch block
System.out.println(e.toString());
e.printStackTrace();
}
}
}
這樣我們會看到控制檯打印出如下信息, 說明我們的消息發送到了Destination,並由容器進行了處理。
消息驅動bean是一個異步消息使用者。當JMS消息到達時,容器激發消息驅動bean。消息驅動bean既沒有本地接口也沒有組件接口。消息驅動bean實例是一個消息驅動bean類的實例。