如今郵件發送功能已是幾乎每一個系統或網址必備的功能了,從用戶註冊的確認到找回密碼再到消息提醒,這些功能廣泛的會用到郵件發送功能。咱們都買過火車票,買完後會有郵件提醒,有時候郵件並非買完票立馬就能收到郵件通知,這個就用到了異步郵件發送。html
那怎麼實現郵件的異步發送呢?java
很顯然,引入MQ是一個不錯的選擇。恰好這段時間在練習ActiveMQ,那就拿activemq來實現異步發送郵件吧。git
1、springboot整合JavaMailSendergithub
在發送異步郵件以前,先來簡單介紹下郵件發送的基本內容,瞭解郵件是怎麼發送的,而後再在此基礎上添加activemq。web
要發送郵件就要用到JavaMail,它是Java官方爲方便Java開發人員在應用程序中實現郵件發送和接收功能而提供的一套標準開發包,它支持常見的郵件協議:SMTP/POP3/IMAP/MIME等。想要發送郵件只須要調用JavaMail的API便可。後來,Spring對於JavaMail進行了封裝,而後springboot又進一步封裝,如今使用起來很是方便。請看代碼:spring
###mail config ### spring.mail.host=smtp.qq.com(配置郵件發送協議) spring.mail.username=xxxx@qq.com(發件人,具體配成你須要的郵箱) spring.mail.password=對於qq郵箱來講,這裏不是密碼,而是受權碼 spring.mail.default-encoding=utf-8 mail.to=xxxx@qq.com (爲了方便,我這裏將收件人統一配置成一個,實際業務中確定按照實際狀況發送的)
至於受權碼的獲取,須要到qq郵箱裏面 設置->帳戶,而後到圖示的地方,開啓服務,而後根據提示獲取受權碼apache
package com.mail.service.impl; import com.mail.service.MailService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.FileSystemResource; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import javax.mail.internet.MimeMessage; import java.io.File; @Service public class MailServiceImpl implements MailService { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private JavaMailSender mailSender;//注入JavaMailSender,具體發送工做須要它完成 @Value("${spring.mail.username}")//從配置文件中獲取發件人郵箱 public String from; /** * 發送普通文本郵件 */ @Override public void sendSimpleMail(String to, String subject, String context){ SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setFrom(from);//發件人 mailMessage.setTo(to);//收件人 mailMessage.setSubject(subject);//郵件主題 mailMessage.setText(context);//郵件正文 mailSender.send(mailMessage);//發送郵件 logger.info("郵件發送成功"); } /** * 發送HTML郵件 */ @Override public void sendMimeMail(String to, String subject, String context){ MimeMessage mailMessage = mailSender.createMimeMessage(); try{//發送非純文本的郵件都須要用的helper來解析 MimeMessageHelper helper = new MimeMessageHelper(mailMessage); helper.setFrom(from); helper.setTo(to); // helper.setBcc("xxxx@qq.com");//抄送人 helper.setSubject(subject); helper.setText(context,true);//這裏的第二個參數要爲true纔會解析html內容 mailSender.send(mailMessage); logger.info("郵件發送成功"); } catch(Exception ex){ logger.error("郵件發送失敗",ex); } } /** * 發送帶附件的郵件 */ @Override public void sendAttachMail(String[] to, String subject, String context, String filePath) { MimeMessage message = mailSender.createMimeMessage(); try{ MimeMessageHelper helper = new MimeMessageHelper(message,true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(context); FileSystemResource file = new FileSystemResource(new File(filePath)); helper.addAttachment(file.getFilename(),file);//添加附件,須要用到FileStstemResource mailSender.send(message); logger.info("帶郵件的附件發送成功"); }catch(Exception ex){ logger.error("帶附件的郵件發送失敗",ex); } } /** * 發送正文帶圖片的郵件 */ @Override public void sendInlineMail(String to, String subject, String context, String filePath, String resId) { MimeMessage message = mailSender.createMimeMessage(); try{ MimeMessageHelper helper = new MimeMessageHelper(message,true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(context,true); FileSystemResource res = new FileSystemResource(new File(filePath)); helper.addInline(resId, res); mailSender.send(message); logger.info("郵件發送成功"); } catch (Exception ex){ logger.error("郵件發送失敗",ex); } } }
代碼中分別對發送普通文本郵件、HTML郵件、代碼附件的郵件、帶圖片的郵件進行了示範json
package com.mail; import com.mail.service.MailService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest public class MailServiceTest { @Autowired MailService mailService; @Value("${mail.to}") private String mailTo; @Test public void testSimpleMail(){ mailService.sendSimpleMail(mailTo,"純文本郵件","你好,這是一封測試郵件"); } @Test public void testMimeMail(){ String context = "<html>\n" + "<body>\n" + "你好,<br>" + "這是一封HTML郵件\n" + "</body>\n" + "</html>"; mailService.sendMimeMail(mailTo,"HTML郵件",context); } @Test public void testSendAttachMail(){ String[] to = {mailTo,這裏是收件人郵箱}; mailService.sendAttachMail(to,"帶附件的郵件","你好,這是一封帶附件的郵件","D:\\1.jpg"); } @Test public void testSendInlineMail(){ String resId = "1"; String context = "<html><body>你好,<br>這是一封帶靜態資源的郵件<br><img src=\'cid:"+resId+"\'></body></html>"; mailService.sendInlineMail(mailTo,"帶靜態圖片的郵件",context,"D:\\1.jpg",resId); } }
郵件發送的代碼基本實現瞭解了,接下來引入activemq的實現。 安全
2、springboot整合ActiveMQ實現異步郵件發送springboot
springboot整合ActiveMQ其實也比較簡單,首先配置文件中須要添加ActiveMQ的相關配置,而後生產者經過注入JmsTemplate發送消息,消費者實現監聽消費。
實現功能後,最終代碼結構:
controller+ActiveMQService扮演生產者角色,發送消息給消費者;
listener扮演消費者角色,接收到消息後調用MailService的接口執行郵件發送。
具體代碼以下:
###queue name### com.sam.mail.queue=com.sam.mail.queue ###activemq config### #mq服務地址 spring.activemq.broker-url=tcp://localhost:61616 spring.activemq.pool.enabled=false #mq用戶名和密碼 spring.activemq.user=admin spring.activemq.password=admin #處理序列化對象須要用到的配置 spring.activemq.packages.trusted=true spring.activemq.packages.trust-all=true
package com.mail.service.impl; import com.alibaba.fastjson.JSON; import com.mail.model.MailBean; import com.mail.service.ActiveMQService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.jms.core.JmsTemplate; import org.springframework.stereotype.Service; @Service public class ActiveMQServiceImpl implements ActiveMQService { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired JmsTemplate template; @Value("${com.sam.mail.queue}") private String queueName; @Override public void sendMQ(String[] to, String subject, String content) { this.sendMQ(to,subject,content,null); } @Override public void sendMQ(String[] to, String subject, String content, String filePath) { this.sendMQ(to,subject,content,filePath,null); } @Override public void sendMQ(String[] to, String subject, String content, String filePath, String srcId) { MailBean bean = new MailBean(); bean.setTo(to); bean.setSubject(subject); bean.setContent(content); bean.setFilePath(filePath); bean.setSrcId(srcId); template.convertAndSend(queueName,bean); logger.info("郵件已經發送到MQ:"+ JSON.toJSONString(bean)); } }
package com.mail.controller; import com.mail.service.ActiveMQService; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author JAVA開發老菜鳥 */ @RestController public class MailSenderController { @Resource ActiveMQService activeMQService; @Value("${mail.to}") private String mailTo; @RequestMapping("/sendSimpleMail.do") public void sendSimpleMail(){ String[] to = {mailTo}; String subject = "普通郵件"; String context = "你好,這是一封普通郵件"; activeMQService.sendMQ(to, subject, context); } @RequestMapping("/sendAttachMail.do") public void sendAttachMail(){ String[] to = {mailTo}; String subject = "帶附件的郵件"; String context = "<html><body>你好,<br>這是一封帶附件的郵件,<br>具體請見附件</body></html>"; String filePath = "D:\\1.jpg"; activeMQService.sendMQ(to, subject, context, filePath); } @RequestMapping("/sendMimeMail.do") public void sendMimeMail(){ String[] to = {mailTo}; String subject = "普通郵件"; String filePath = "D:\\1.jpg"; String resId = "1.jpg"; String context = "<html><body>你好,<br>這是一封帶圖片的郵件,<br>請見圖片<br><img src=\'cid:"+resId+"\'></body></html>"; activeMQService.sendMQ(to, subject, context, filePath, resId); } }
public class MailBean implements Serializable { private String from;//發件人 private String[] to;//收件人列表 private String subject;//郵件主題 private String content;//郵件正文 private String filePath;//文件(圖片)路徑 private String srcId;//圖片名 ...... getter/setter略 ...... }
package com.mail.listener; import com.mail.model.MailBean; import com.mail.service.MailService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jms.annotation.JmsListener; import org.springframework.stereotype.Service; import javax.jms.ObjectMessage; import java.io.Serializable; /** * 監聽到MQ後調用mailService執行郵件發送操做 */ @Service public class SendMailMQListener { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired MailService mailService; /** * 經過監聽目標隊列實現功能 */ @JmsListener(destination = "${com.sam.mail.queue}") public void dealSenderMailMQ(ObjectMessage message){ try{ Serializable object = message.getObject(); MailBean bean = (MailBean) object; mailService.sendMail(bean.getTo(),bean.getSubject(),bean.getContent(),bean.getFilePath(),bean.getSrcId()); logger.error("消費者消費郵件信息成功"); } catch (Exception ex){ logger.error("消費者消費郵件信息失敗:"+ ex); } } }
@Override public void sendMail(String[] to, String subject, String context, String filePath, String resId ){ MimeMessage message = mailSender.createMimeMessage(); try{ MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(context, true); if(!StringUtils.isEmpty(filePath) && !StringUtils.isEmpty(resId)){//文件路徑和resId都不爲空,視爲靜態圖片 FileSystemResource resource = new FileSystemResource(new File(filePath)); helper.addInline(resId, resource); } else if(!StringUtils.isEmpty(filePath)){//只有文件路徑不爲空,視爲附件 FileSystemResource resource = new FileSystemResource(new File(filePath)); helper.addAttachment(resource.getFilename(),resource); } mailSender.send(message); logger.info("郵件發送成功"); } catch (Exception ex){ logger.error("郵件發送錯誤:", ex); }
查看下mq的頁面控制檯
至此,功能已經實現。
3、遇到過的問題
在實現這個demo的時候,遇到了一些問題,也把它們列出來,給別人一個參考
第一個問題:
消費者消費郵件信息失敗:javax.jms.JMSException: Failed to build body from content. Serializable class not available to broker. Reason: java.lang.ClassNotFoundException: Forbidden class com.mail.model.MailBean! This class is not trusted to be serialized as ObjectMessage payload. Please take a look at http://activemq.apache.org/objectmessage.html for more information on how to configure trusted classes.
This class is not trusted to be serialized as ObjectMessage payload,是說個人MailBean對象不是能夠新人的序列化對象,
緣由:
傳遞對象消息時 ,ActiveMQ的ObjectMessage依賴於Java的序列化和反序列化,可是這個過程被認爲是不安全的。具體信息查看報錯後面的那個網址:
http://activemq.apache.org/objectmessage.html
解決方法:
在application.properties文件中追加下面的配置便可
spring.activemq.packages.trust-all=true
第二個問題:
*************************** APPLICATION FAILED TO START *************************** Description: A component required a bean of type 'com.mail.service.ActiveMQService' that could not be found. Action: Consider defining a bean of type 'com.mail.service.ActiveMQService' in your configuration.
緣由:
ActiveMQService沒有被spring掃描並初始化,而後我在代碼用經過@Autowaired註解使用獲取不到。 找了以後發現是個人@Service註解放到了interface上,應該放到service的impl類上。
解決方法:
將@Service註解放到impl類上
好,以上就是Springboot+ActiveMQ+JavaMail實現異步郵件發送的所有內容了,
以爲有幫助的話,記得點贊哦~~
原文出處:https://www.cnblogs.com/sam-uncle/p/11032453.html