javascript
018_Spring+ActiveMQ(消息中间件)
1. 配置連接工廠bean
1.1. 我們要想發送消息到ActiveMQ, 就需要創建客戶和提供者之間的連接, 連接工廠(ActiveMQConnectionFactory)可以完成這個工作。
1.2. 可以配置spring包下的org.apache.activemq.spring.ActiveMQConnectionFactory
1.3. org.apache.activemq.spring.ActiveMQConnectionFactory類在下面這個jar包中?
1.4. org.apache.activemq.spring.ActiveMQConnectionFactory繼承自org.apache.activemq.ActiveMQConnectionFactory?
1.5. 在org.apache.activemq.spring.ActiveMQConnectionFactory中, 可以使用brokerURL屬性來指定ActiveMQ的地址和端口號。?
2. 配置目的地對象
2.1. 除了連接工廠外, 我們還需要消息傳遞的目的地。目的地可以是一個隊列, 也可以是一個主題, 這取決于應用的需求。
2.2. 隊列模式
2.3. 主題模式?
2.4. 在org.apache.activemq.command.ActiveMQQueue或 org.apache.activemq.command.ActiveMQTopic中, 可以使用physicalName屬性來指定隊列或主題的名字。?
2.5. 如果消息目的地是隊列模式, 也可以不配置(不推薦), 在發送和接收消息的時候指定目的地名稱即可。在發送和接收消息的時候設置一個名稱, 但它只是一個名稱, 它并沒有說明你所處理的目的地是什么類型, 如果已經存在該名稱的隊列或主題的話, 就會使用已有的; 如果尚未存在的話, 將會創建一個新的目的地, 通常會是隊列。
3. 配置模板類
3.1. 針對如何消除冗長和重復的JMS代碼, Spring給出的解決方案是JmsTemplate。JmsTemplate可以創建連接、獲得會話、發送和接收消息。
3.2. 因為JmsTemplate需要知道如何連接到消息提供者, 所以我們必須為connection-Factory屬性設置實現了JMS的ConnectionFactory接口的bean引用。?
3.3. 在org.springframework.jms.core.JmsTemplate中, 可以使用defaultDestination屬性來指定默認目的地。這樣就不必在每次發送和接收消息的時候指定目的地。當然我們這里也可以不必配置目的地, 然后在發送和接收消息的時候指定目的地。?
3.4. org.springframework.jms.core.JmsTemplate類實現了org.springframework.jms.core.JmsOperations接口。?
3.5. 另外, JmsTemplate可以處理所有拋出的笨拙的JMSException異常。如果在使用JmsTemplate時拋出JMSException異常, JmsTemplate將捕獲該異常, 然后拋出一個非檢查型異常(JMSException都是檢查型異常, 因此必須捕獲), 該異常是Spring自帶的, 是JmsException異常的子類。?
4. 發送消息
4.1. send()方法發送消息, 第一個是消息的目的地(可選參數), 第二個便是具體的消息。我們使用MessageCreator(在這里的實現是作為一個匿名內部類)來構造消息, 在MessageCreator的createMessage()方法中, 有一個session對象, session對象可以創建各種各樣的消息(文本消息、流消息、映射消息、對象消息等), 然后返回消息對象。
4.2. JmsTemplate還提供了convertAndSend()方法, 發送消息時, 對消息進行轉換。與send()方法不同, convertAndSend()方法并不需要MessageCreator作為參數, 這是因convertAndSend()會使用內置的消息轉換器(message converter)為我們創建消息。?
4.3. 在發送之前轉換為Message, JmsTemplate內部會進行一些處理, 它使用一個MessageConverter的實現類將對象轉換為Message。MessageConverter是Spring定義的接口, 只有兩個需要實現的方法:?
4.4. 默認的轉換器?
4.5. MappingJackson2MessageConverter: 使用Jackson JSON庫實現消息和JSON格式之間的相互轉換。
4.6. MarshallingMessageConverter: 使用JAXB庫實現消息和XML格式之間的相互轉換。
4.7. SimpleMessageConverter: 實現String和TextMessage之間的轉換、字節數組和BytesMessage之間的轉換、Map和MapMessage之間的轉換、Serializable對象和ObjectMessage之間的轉換。
4.8. 默認情況下, JmsTemplate在convertAndSend()方法中會使用SimpleMessageConverter。如果你想使用JSON消息的話, 那么可以聲明一個MappingJackson2MessageConverter類型的Bean, 然后把它注入到JmsTemplate的messageConverter屬性中。
5. 接收消息
5.1. receive()方法接收消息, 它有一個可選的消息目地參數。當調用JmsTemplate的receive()方法時, JmsTemplate會嘗試從消息代理中獲取一個消息, 如果沒有可用的消息, receive()方法會一直等待, 直到獲得消息為止。
5.2. 這里有一點需要注意, 當調用message.getText()方法時會拋出JMSException, 這個異常是屬于JMS API的。JMSException是一個檢查異常, 在JMS操作中會拋出各種各樣的JMSException, 但是前面我們使用JmsTemplate時并沒有捕獲任何JMSException, 是因為JmsTemplate內部已經將需要檢查的JMSException轉換成了非檢查的Spring自己的JmsException。在上面代碼中因為調用的是message.getText()方法而不是JmsTemplate的方法, 所以我們需要捕獲JMSException。但是按照Spring的設計理念, 我們應該盡量減少檢查異常, 所以在catch塊里面我們又通過JmsUtils工具把JMSException轉換成了非檢查的JmsException。
5.3. JmsTemplate同樣提供了receiveAndConvert()方法, 在接收消息時, 對消息進行轉換。因為使用的是JmsTemplate的方法, 所以我們不需要再捕獲JMSException檢查異常。這個接收消息代碼只有一行有效代碼, 非常簡潔。
5.4. 不管使用msTemplate的receive()還是receiveAndConvert()方法消費消息, 它們都是同步的, 也就是說接收者在消息到達時需要等待。
6. 異步接收消息
6.1. 要想在消息出現時得到通知, 那么就需要一個監聽器監聽queue或者topic。Spring提供了以POJO的方式處理消息的能力, 不需要依賴任何接口。在EJB中, message driven bean(MDB)就可以實現異步的處理消息。Spring在這方面參考了EJB3對MDB的實現, 不過在Spring中我們把它稱作消息驅動POJO, 也就是message-driven POJO(MDP)。
6.2. 賦予上面POJO接收消息能力的關鍵在于將其配置成一個Spring消息監聽器, Spring的jms命名空間提供了所有相關配置。首先, 我們現需要把上面的POJO對象聲明成一個bean。其次, 把MessageHandler變成一個消息驅動POJO, 即把這個bean聲明成一個listener。?
7. 異步接收消息-實現MessageListener接口
7.1. MessageListener接口
7.2. 我們的MessageHandler還可以實現一個MessageListener接口, 這樣的話就不需要再單獨指定消息處理的方法了, MyMessageListener的onMessage()方法會自動被調用。?
7.3. 然后直接配置listener即可(不用再配置method方法屬性)。?
8. 對象序列化包名的白名單限制
8.1. ActiveMQ對序列化有包名的白名單限制, 可以把包名加入到配置中, 或者關閉掉白名單限制。
9. SpringJMS發送文本消息例子
9.1. 新建一個名為SpringJMSText的Java項目, 拷入相關jar包。
9.2. 新建JmsQueueSender.java
package com.lywgames.jms;import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import org.springframework.jms.core.JmsOperations; import org.springframework.jms.core.MessageCreator;public class JmsQueueSender {private JmsOperations jmsOperations;public void setJmsOperations(JmsOperations jmsOperations) {this.jmsOperations = jmsOperations;}public void sendMessage(String message) {jmsOperations.send("myspringjmsqueue", new MessageCreator() {@Overridepublic Message createMessage(Session session) throws JMSException {return session.createTextMessage(message);}});} }9.3. 新建JmsQueueReceiver.java
package com.lywgames.jms;import javax.jms.JMSException; import javax.jms.TextMessage; import org.springframework.jms.core.JmsOperations; import org.springframework.jms.support.JmsUtils;public class JmsQueueReceiver { private JmsOperations jmsOperations;public void setJmsOperations(JmsOperations jmsOperations) {this.jmsOperations = jmsOperations;}public void receiveMessage() {TextMessage msg = (TextMessage) jmsOperations.receive("myspringjmsqueue");try {// 非jms模板拋出的異常System.out.println(msg.getText());} catch (JMSException e) {e.printStackTrace();JmsUtils.convertJmsAccessException(e);}} }9.4. 在src目錄下配置applicationContext.xml
?9.5. 新建Test.java
package com.lywgames.jms;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {// 1. 類路徑加載配置文件ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 獲取JmsQueueSenderJmsQueueSender jmsQueueSender = context.getBean(JmsQueueSender.class);// 3. 調用JmsQueueSender的sendMessage方法, 發送消息jmsQueueSender.sendMessage("Spring JMS發送文本消息。");// 4. 獲取JmsQueueReceiverJmsQueueReceiver jmsQueueReceiver = context.getBean(JmsQueueReceiver.class);// 5. 調用JmsQueueReceiver的receiveMessage方法, 獲取消息jmsQueueReceiver.receiveMessage();// 6. 關閉應用程序上下文context.close();} }9.6. 運行項目效果圖
10. SpringJMS發送對象消息例子
10.1. 新建一個名為SpringJMSObject的Java項目, 拷入相關jar包。
10.2. 新建User.java
package com.lywgames.jms;import java.io.Serializable;public class User implements Serializable {private static final long serialVersionUID = 1L;private String userName;private String password;public User() {}public User(String userName, String password) {this.userName = userName;this.password = password;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User [userName=" + userName + ", password=" + password + "]";}}10.3. 新建JmsQueueSender.java
package com.lywgames.jms;import org.springframework.jms.core.JmsTemplate;public class JmsQueueSender {private JmsTemplate jmsTemplate;public void setJmsTemplate(JmsTemplate jmsTemplate) {this.jmsTemplate = jmsTemplate;}public void sendMessage(Object message) {jmsTemplate.convertAndSend(message);} }10.4. 新建JmsQueueReceiver.java
package com.lywgames.jms;import org.springframework.jms.core.JmsTemplate;public class JmsQueueReceiver {private JmsTemplate jmsTemplate;public void setJmsTemplate(JmsTemplate jmsTemplate) {this.jmsTemplate = jmsTemplate;}public void receiveMessage() {User user = (User) jmsTemplate.receiveAndConvert();System.out.println(user);} }10.5. 在src目錄下配置applicationContext.xml
10.6. 新建Test.java
package com.lywgames.jms;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {// 1. 類路徑加載配置文件ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 獲取JmsQueueSenderJmsQueueSender jmsQueueSender = context.getBean(JmsQueueSender.class);// 3. 調用JmsQueueSender的sendMessage方法, 發送消息jmsQueueSender.sendMessage(new User("zhangsan", "123456"));// 4. 獲取JmsQueueReceiverJmsQueueReceiver jmsQueueReceiver = context.getBean(JmsQueueReceiver.class);// 5. 調用JmsQueueReceiver的receiveMessage方法, 獲取消息jmsQueueReceiver.receiveMessage();// 6. 關閉應用程序上下文context.close();} }10.7. 運行項目效果圖
11. SpringJMS消息和JSON格式轉換例子
11.1. 新建一個名為SpringJMSJson的Java項目, 拷入相關jar包。
11.2. 新建User.java
package com.lywgames.jms;import java.io.Serializable;public class User implements Serializable {private static final long serialVersionUID = 1L;private String userName;private String password;public User() {}public User(String userName, String password) {this.userName = userName;this.password = password;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "User [userName=" + userName + ", password=" + password + "]";}}11.3. 新建Product.java
package com.lywgames.jms;import java.io.Serializable;public class Product implements Serializable {private static final long serialVersionUID = 1L;private Integer id;private String name;public Product() {}public Product(Integer id, String name) {this.id = id;this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}}11.4. 新建MyJsonConverter.java
package com.lywgames.jms;import java.util.HashMap; import java.util.Map; import org.springframework.jms.support.converter.MappingJackson2MessageConverter; import org.springframework.jms.support.converter.MessageType;public class MyJsonConverter extends MappingJackson2MessageConverter {public MyJsonConverter() {// 定義typeId到Class的映射Map<String, Class<?>> typeIdMappings = new HashMap<String, Class<?>>();typeIdMappings.put(User.class.getName(), User.class);typeIdMappings.put(Product.class.getName(), Product.class);// 消息是文本類型this.setTargetType(MessageType.TEXT);// 設置typeId和Class的映射this.setTypeIdMappings(typeIdMappings);// 發送到目的地的TypeId的名稱 // this.setTypeIdPropertyName(Product.class.getName());} }11.5. 新建JmsQueueSender.java
package com.lywgames.jms;import org.springframework.jms.core.JmsTemplate;public class JmsQueueSender {private JmsTemplate jmsTemplate;public void setJmsTemplate(JmsTemplate jmsTemplate) {this.jmsTemplate = jmsTemplate;}public void sendMessage(Object message, String typeIdPropertyName) {MyJsonConverter converter = (MyJsonConverter) jmsTemplate.getMessageConverter();converter.setTypeIdPropertyName(typeIdPropertyName);jmsTemplate.convertAndSend(message);} }11.6. 新建JmsQueueReceiver.java
package com.lywgames.jms;import javax.jms.JMSException; import org.apache.activemq.command.ActiveMQTextMessage; import org.springframework.jms.core.JmsTemplate;public class JmsQueueReceiver {private JmsTemplate jmsTemplate;public void setJmsTemplate(JmsTemplate jmsTemplate) {this.jmsTemplate = jmsTemplate;}public void receiveMessage(String typeIdPropertyName) {try {MyJsonConverter converter = (MyJsonConverter) jmsTemplate.getMessageConverter();converter.setTypeIdPropertyName(typeIdPropertyName);// 這里不能使用receiveAndConvert()方法ActiveMQTextMessage obj = (ActiveMQTextMessage) jmsTemplate.receive();System.out.println(obj.getText());} catch (JMSException e) {e.printStackTrace();}} }11.7. 在src目錄下配置applicationContext.xml
11.8. 新建Test.java
package com.lywgames.jms;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {// 1. 類路徑加載配置文件ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 獲取JmsQueueSenderJmsQueueSender jmsQueueSender = context.getBean(JmsQueueSender.class);// 3. 調用JmsQueueSender的sendMessage方法, 發送消息jmsQueueSender.sendMessage(new User("李四", "123456"), User.class.getName()); // jmsQueueSender.sendMessage(new Product(1001, "牛奶"), Product.class.getName());// 4. 獲取JmsQueueReceiverJmsQueueReceiver jmsQueueReceiver = context.getBean(JmsQueueReceiver.class);// 5. 調用JmsQueueReceiver的receiveMessage方法, 獲取消息jmsQueueReceiver.receiveMessage(User.class.getName());// 6. 關閉應用程序上下文context.close();} }11.9. 運行項目效果圖
12. SpringJMS異步接收消息例子
12.1. 新建一個名為SpringJMSMap的Java項目, 拷入相關jar包。
12.2. 新建JmsQueueSender.java
package com.lywgames.jms;import org.springframework.jms.core.JmsTemplate;public class JmsQueueSender {private JmsTemplate jmsTemplate;public void setJmsTemplate(JmsTemplate jmsTemplate) {this.jmsTemplate = jmsTemplate;}public void sendMessage(Object message) {jmsTemplate.convertAndSend(message);} }12.3. 新建AnsyJmsQueueReceiver.java
package com.lywgames.jms;import java.util.Map;public class AnsyJmsQueueReceiver {public void handleMessage(Map<String, Object> message){System.out.println(message);} }12.4. 在src目錄下配置applicationContext.xml
12.5. 新建Test.java
package com.lywgames.jms;import java.util.HashMap; import java.util.Map; import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {public static void main(String[] args) {// 1. 類路徑加載配置文件ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 2. 獲取JmsQueueSenderJmsQueueSender jmsQueueSender = context.getBean(JmsQueueSender.class);Map<String, Object> map = new HashMap<String, Object>();map.put("id", 100000000000L);map.put("name", "lisi");// 3. 調用JmsQueueSender的sendMessage方法, 發送消息jmsQueueSender.sendMessage(map);// 4. 關閉應用程序上下文context.close();} }12.6. 運行項目效果圖?
總結
以上是生活随笔為你收集整理的018_Spring+ActiveMQ(消息中间件)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 017_Spring+Mybatis+C
- 下一篇: 001_Maven入门