上節簡單描述了一下JMS的概念,這節來寫個小demo。這樣可以比較直觀的看到JMS通信的過程。
前期回顧:04_Java通信_JMS概念
在開發這個demo前,首先要下載ActiveMQ來作爲消息服務器。
Demo實現功能
這是我們要實現的一個發佈/訂閱模型的demo
A發送消息到消息服務器ActiveMQ,然後ActiveMQ將消息發給訂閱這個Topic的客戶端:A和B
開發流程:
1.要開發一個jms demo首先要下載某廠商的JMS的實現
2.下載ActiveMQ,這是Apache的一個開源MQ系統
下載地址:http://activemq.apache.org/download.html
下載歷史版本:http://activemq.apache.org/download-archives.html
3.下載後直接解壓
我們開發一個demo直接可以使用這個activemq-all-5.2.0.jar這個jar包
4.還需要jms的jar包:
如果使用Maven,可以使用下面的依賴:
<dependency> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> <version>1.1</version> </dependency>
5.開發一個簡單的聊天demo,就想上面那張聊天圖一樣
package com.jms.test; import java.io.BufferedReader; import java.io.InputStreamReader; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicConnectionFactory; import javax.jms.TopicPublisher; import javax.jms.TopicSession; import javax.jms.TopicSubscriber; import javax.naming.Context; import javax.naming.InitialContext; public class Chat implements javax.jms.MessageListener { private TopicSession pubSession; private TopicPublisher publisher; private TopicConnection conntection; private String username; public Chat(String topicFactory,String topicName,String username) throws Exception{ // System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.activemq.jndi.ActiveMQInitialContextFactory"); // System.setProperty(Context.PROVIDER_URL, "tcp://localhost:61616"); // //使用JNDI獲得ConnectionFactory InitialContext ctx = new InitialContext(); TopicConnectionFactory conFactory = (TopicConnectionFactory)ctx.lookup(topicFactory); //通過ConnectionFactory得到Connection TopicConnection connection = conFactory.createTopicConnection(); //得到session TopicSession pubSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); TopicSession subSession = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); //使用JNDI查找主題Topic Topic chatTopic = (Topic)ctx.lookup(topicName); //通過session和主題Topic獲得發佈者,訂閱者 TopicPublisher publisher = pubSession.createPublisher(chatTopic); TopicSubscriber subscriber = subSession.createSubscriber(chatTopic); subscriber.setMessageListener(this); this.conntection = connection; this.pubSession = pubSession; this.publisher = publisher; this.username = username; conntection.start(); } @Override public void onMessage(Message m) { try { // m.set TextMessage tm = (TextMessage)m; System.out.println(tm.getText()); } catch (JMSException e) { e.printStackTrace(); } } protected void writeMessage(String text) throws Exception{ TextMessage message = pubSession.createTextMessage(); message.setText(username+": "+text); //消息發送者,發送消息 publisher.publish(message); } public void close() throws Exception{ conntection.close(); } public static void main(String[] args) throws Exception { Chat chat = new Chat("TopicCF","topic1","hh"); BufferedReader commandLine = new BufferedReader(new InputStreamReader(System.in)); while(true){ String s = commandLine.readLine(); if(s.equalsIgnoreCase("exit")){ chat.close(); System.exit(0); }else{ chat.writeMessage(s); } } } }
6.配置文件:
java.naming.factory.initial =org.apache.activemq.jndi.ActiveMQInitialContextFactory java.naming.provider.url =tcp://localhost:61616 java.naming.security.principal =system java.naming.security.credentials =manager connectionFactoryNames =TopicCF topic.topic1 =jms.topic1
7.客戶端B:
package com.jms.test; import java.io.BufferedReader; import java.io.InputStreamReader; public class ChatA { public static void main(String[] args) throws Exception { Chat chat = new Chat("TopicCF","topic1","cc"); BufferedReader commandLine = new BufferedReader(new InputStreamReader(System.in)); while(true){ String s = commandLine.readLine(); if(s.equalsIgnoreCase("exit")){ chat.close(); System.exit(0); }else{ chat.writeMessage(s); } } } }
8.如何運行代碼:
啓動消息服務器ActiveMQ
進入ActiveMQ的bin目錄
雙擊運行:activemq.bat
查看是否啓動成功:cmd-->netstat -na|find "61616"
出現:
則啓動成功。還可以登陸
admin:http://127.0.0.1:8161/admin/
demo:http://127.0.0.1:8161/demo/
查看。
將配置文件jndi.properties放在classes的根目錄。
項目目錄結構:
代碼難點分析:
1.在前面已經介紹過JNDI。我們可以通過JNDI來獲取通信對象。
JMS客戶端使用一個目錄服務(JNDI)來訪問ConnectionFactory和Destination(主題和隊列)對象。也就是說這兩個對象JMS API無法獲得。在這一點上,它和連接,會話,生產者,消費者及消息不同。連接,會話,生產者,消費者及消息都是JMS API內部使用工廠模式生產的。JNDI爲了獲得ConnectionFactory和Destination對象提供了一種方便、位置透明、可配置並且可移植的機制,這些對象也稱爲JMS受管對象,因爲它是由系統管理員建立和配置的
2.線程和會話
在程序中創建了兩個topicSession:pubSession,subSession。
爲什麼要創建兩個呢,因爲JMS規定一個session不能同時在一個以上的線程中運行。
這個例子中有兩個線程:
運行writeMessage的主線程(線程所有者:chat應用程序)
運行onMessage的處理線程(線程所有者是JMS提供者所有,即:ActionMQ)