對於WEB系統,向用戶發送郵件、短信、站內信等幾乎是必備的基礎功能,但這些任務相對於所見即所得的當即響應式的請求對實時性的要求並不高,同時任務處理的量還很大。在複雜多系統的情形下,還要考慮多個子系統的通訊問題。不管是從實際業務需求仍是從軟件工程的設計角度出發,消息通訊都有必要成爲一個獨立的模塊。本文以一個很是簡單的業務場景爲例,即系統給用戶發送通知郵件,來說一講如何構建簡單的消息通訊。html
在上一次的博客中咱們講述了消息隊列,消息隊列是消息通訊系統的重要組成部分。J2EE爲運行在jvm虛擬機上的程序間的通訊早就制定了一套標準,也就是咱們提到的JMS標準。但JMS並不涉及到具體實現,咱們在本文中採用應用最爲普遍的ActiveMQ爲例。java
首先咱們須要在pom.xml中引入相關依賴。spring
<!-- JMS --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-core</artifactId> <version>5.7.0</version> </dependency>
而後咱們要在ApplicationContext.xml文件中做出相關配置。apache
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <description>JMS簡單應用配置</description> <!-- ActiveMQ 鏈接工廠 --> <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="${jms.broker_url}" /> </bean> <!-- Spring Caching 鏈接工廠 --> <bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"> <property name="targetConnectionFactory" ref="connectionFactory" /> <property name="sessionCacheSize" value="10" /> </bean> <!-- Queue定義 --> <bean id="notifyQueue" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="q.notify" /> </bean> <!-- Topic定義 --> <bean id="notifyTopic" class="org.apache.activemq.command.ActiveMQTopic"> <constructor-arg value="t.notify" /> </bean> <!-- Spring JMS Template --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="cachingConnectionFactory" /> </bean> <!-- 使用Spring JmsTemplate的消息生產者 --> <bean id="notifyMessageProducer" class="jseed.util.modules.jms.NotifyMessageProducer"> <property name="jmsTemplate" ref="jmsTemplate" /> <property name="notifyQueue" ref="notifyQueue" /> <property name="notifyTopic" ref="notifyTopic" /> </bean> <!-- 異步接收Queue消息Container --> <bean id="queueContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="notifyQueue" /> <property name="messageListener" ref="notifyMessageListener" /> <property name="concurrentConsumers" value="10" /> </bean> <!-- 異步接收Topic消息Container --> <bean id="topicContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="notifyTopic" /> <property name="messageListener" ref="notifyMessageListener" /> </bean> <!-- 異步接收消息處理類 --> <bean id="notifyMessageListener" class="jseed.util.modules.jms.NotifyMessageListener" /> </beans>
對於上文咱們將一一解釋:服務器
ConnectionFactory:用於產生到JMS服務器的連接session
targetConnectionFactory:真正能夠產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供,在這裏是ActiveMQ併發
JmsTemplate:實現消息發送的工具,由Spring提供異步
Destination:用來表示目的地的接口在ActiveMQ中實現了兩種類型的Destination,一個是點對點的ActiveMQQueue,另外一個就是支持訂閱/發佈模式的ActiveMQTopic。,沒有任何方法定義,只是用來作一個標識而已。jvm
在這裏生產者負責生成包含郵件收件人和內容等信息的消息並存入隊列,而消費者則負責從這些信息中國生成郵件併發送出去。ide
生產者代碼
public class NotifyMessageProducer { private JmsTemplate jmsTemplate; private Destination notifyQueue; private Destination notifyTopic; public void sendQueue(final User user) { sendMessage(user, notifyQueue); } public void sendTopic(final User user) { sendMessage(user, notifyTopic); } /** * 使用jmsTemplate最簡便的封裝convertAndSend()發送Map類型的消息. */ private void sendMessage(User user, Destination destination) { Map map = new HashMap(); map.put("userName", user.getName()); map.put("email", user.getEmail()); jmsTemplate.convertAndSend(destination, map); } public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } public void setNotifyQueue(Destination notifyQueue) { this.notifyQueue = notifyQueue; } public void setNotifyTopic(Destination nodifyTopic) { this.notifyTopic = nodifyTopic; } }
消費者代碼
public class NotifyMessageListener implements MessageListener { private static Logger logger = LoggerFactory.getLogger(NotifyMessageListener.class); @Autowired(required = false) private MailService simpleMailService; /** * MessageListener回調函數. */ @Override public void onMessage(Message message) { try { MapMessage mapMessage = (MapMessage) message; // 打印消息詳情 logger.info("UserName:{}, Email:{}", mapMessage.getString("userName"), mapMessage.getString("email")); // 發送郵件 if (simpleMailService != null) { simpleMailService.sendNotificationMail(mapMessage.getString("userName")); } } catch (Exception e) { logger.error("處理消息時發生異常.", e); } } }
郵件模型
public class ApplicationEmail implements Serializable { public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getCc() { return cc; } public void setCc(String cc) { this.cc = cc; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } /**收件人**/ private String address; /**抄送給**/ private String cc; /**郵件主題**/ private String subject; /**郵件內容**/ private String content; /**附件**/ //private MultipartFile[] attachment = new MultipartFile[0]; }
郵件幫助類(此處採用了java mail依賴包)
public class MailService { private static Logger logger = LoggerFactory.getLogger(MailService.class); private JavaMailSender mailSender; private String textTemplate; /** * 發送純文本的用戶修改通知郵件. */ public void sendNotificationMail(String userName) { SimpleMailMessage msg = new SimpleMailMessage(); msg.setFrom("suemi94@qq.com"); msg.setTo("suemi994@gmail.com"); msg.setSubject("用戶修改通知"); // 將用戶名與當期日期格式化到郵件內容的字符串模板 String content = String.format(textTemplate, userName, new Date()); msg.setText(content); try { mailSender.send(msg); if (logger.isInfoEnabled()) { logger.info("純文本郵件已發送至{}", StringUtils.join(msg.getTo(), ",")); } } catch (Exception e) { logger.error("發送郵件失敗", e); } } /** * 同步發送郵件 * * @param email * @throws MessagingException * @throws IOException */ public void sendMailBySynchronizationMode(ApplicationEmail email) throws MessagingException, IOException { Session session=Session.getDefaultInstance(new Properties()); MimeMessage mime= new MimeMessage(session); MimeMessageHelper helper = new MimeMessageHelper(mime, true, "utf-8"); helper.setFrom("suemi94@qq.com");//發件人 helper.setTo(InternetAddress.parse(email.getAddress()));//收件人 helper.setReplyTo("suemi94@qq.com");//回覆到 helper.setSubject(email.getSubject());//郵件主題 helper.setText(email.getContent(), true);//true表示設定html格式 mailSender.send(mime); } /** * Spring的MailSender. */ public void setMailSender(JavaMailSender mailSender) { this.mailSender = mailSender; } /** * 郵件內容的字符串模板. */ public void setTextTemplate(String textTemplate) { this.textTemplate = textTemplate; } }