本文主要是講述JavaMail 和 消息隊列的一些簡單應用,這是第一次寫博客,有很多不足的地方希望各位看客給出寶貴建議,另外本文寫的不對的地方,請各位大神勿噴!!!
很多人都會在各個系統里面遇到發送郵件的功能,這次開博寫文章也是在整理過程中發現,團隊中有個新來的小童鞋對發送郵件這塊不是很熟,所以自己准備寫一個簡單的樣例。
這里加了一個消息隊列主要是用於高並發的情況下,對郵件發送的控制,並可以對消息進行緩存,防止消息丟失。
話不多說直接上代碼:
用maven對項目進行的管理,pom.xml
<dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency>
郵件實體:
package com.test.utils.mail; import java.io.Serializable; /** * * @author * */ public class MailVo { /** * 注釋內容 */ private static final long serialVersionUID = 1L; private String receiver; private String subject; private String content; public String getReceiver() { return receiver; } public void setReceiver(String receiver) { this.receiver = receiver; } 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; } public static long getSerialversionuid() { return serialVersionUID; } }
郵件基本配置config.properties
#mail smtp host mail.smtp.host = smtp.exmail.qq.com mail.smtp.port = 465 mail.smtp.username = test mail.sender = test@qq.com mail.smtp.password = test
很多人用的是多線程來做的這個地方,我考慮了下,我覺得用隊列比較合適,因為如果有大量需要發送郵件的時候,新建太多的線程會對系統產生不必要的開銷,雖然可以集合線程池的方式來做,當然如果是郵件業務比較多,多線程的並發會好點。
我考慮了下,有時候郵件業務不是很多,所以,我給單線程加了個狀態維護,在3分鍾內沒有任務,則銷毀該線程,等待新的任務再重新創建新的線程。
package com.test.utils.mail; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; /** * 郵件發送管理插件 * 主要用於管理郵件隊列和發送郵件 * @author * */ public class SendMailManager{ private static Logger logger = Logger.getLogger(SendMailManager.class); //緩存發送郵件的隊列 public static BlockingQueue<MailVo> queue = new ArrayBlockingQueue<MailVo>(2000); //發送郵件線程狀態 private static boolean isRunning = false; //超時時間間隔 public static long TIMEOUT_INTERVAL = 3*60*1000; public static boolean sendMail(MailVo mail){ boolean rsp = false; try { //阻塞3秒 rsp = queue.offer(mail, 3, TimeUnit.SECONDS); if(!rsp){ throw new Exception("服務器發送郵件繁忙,請稍后再發"); } if(!isRunning){ startup(); isRunning = true; } } catch (Exception e) { logger.error("添加發送郵件隊列出錯", e); } return rsp; } public static void startup(){ if(isRunning){ return; } Thread th = new Thread(){ @Override public void run(){ logger.debug("郵件發送消息線程啟動"); long timestamp = System.currentTimeMillis(); while(isRunning) { long timeout = timestamp + TIMEOUT_INTERVAL; if(System.currentTimeMillis()>timeout) { //空跑一段時間(3分鍾)后線程退出 break; } try { if(queue.size()==0) { Thread.sleep(1000); continue; } timestamp = System.currentTimeMillis(); MailVo mail = queue.poll(); MailUtil.sendMail(mail); //發送一個郵件休息2秒,防止發送過快,導致主油箱被鎖定 Thread.sleep(2000); } catch (Exception e) { logger.error("郵件推送線程出錯",e); } } isRunning = false; logger.debug("郵件發送線程停止。"); } }; th.start(); } }
郵件發送工具:
package com.test.utils.mail; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.List; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import org.apache.log4j.Logger; import org.springframework.util.StringUtils; import com.sun.mail.smtp.SMTPSendFailedException; import com.sun.mail.util.MailSSLSocketFactory; import com.test.constant.Config; import com.test.utils.PropUtil; /** * @描述: * * @author 作者 : * */ public class MailUtil { private static Logger logger = Logger.getLogger(MailUtil.class); public static boolean sendMail(MailVo mailVo){ boolean flag = false; PropUtil pro = new PropUtil(Config.CONFIG_PATH); String smtpHost = ""; String smtpport = ""; String from = ""; String password = ""; String to = ""; String subject = ""; String messageText = ""; try { smtpHost = pro.getValue(Config.MAIL_SMTP_HOST); smtpport = pro.getValue(Config.MAIL_SMTP_PORT); from = pro.getValue(Config.MAIL_SENDER); password = pro.getValue(Config.MAIL_SMTP_PASSWORD); to = mailVo.getReceiver(); subject = mailVo.getSubject(); messageText = mailVo.getContent(); int sendFlag = sendMessage(smtpHost, smtpport, from, password, to, subject, messageText); flag = true; if(sendFlag == 200){ logger.debug("郵件發送成功:發件人 = " + from + "; 收件人 = " + to + "; 主題 = " + subject + "; 內容 = " + messageText); }else{ logger.debug("郵件發送失敗:發件人 = " + from + "; 收件人 = " + to + "; 主題 = " + subject + "; 內容 = " + messageText); } } catch (Exception e) { logger.debug("發送郵件失敗,失敗內容:smtpHost = " + smtpHost); logger.debug("發送郵件失敗,失敗內容:smtpport = " + smtpport); logger.debug("發送郵件失敗,失敗內容:from = " + from); logger.debug("發送郵件失敗,失敗內容:password = " + password); logger.debug("發送郵件失敗,失敗內容:to = " + to); logger.debug("發送郵件失敗,失敗內容:smtpHost = " + smtpHost); logger.debug("發送郵件失敗,失敗內容:subject = " + subject); logger.debug("發送郵件失敗,失敗內容:messageText = " + messageText); logger.error("發送郵件失敗", e); } return flag; } public static int sendMessage(String smtpHost,String smtpport, String from, String password, String to, String subject, String messageText){ try { //1、配置郵件基本屬性 java.util.Properties props = new java.util.Properties(); //2、判斷是否需要開啟SSL if(!StringUtils.isEmpty(smtpport) && smtpport.equals("465")){ MailSSLSocketFactory sf; try { sf = new MailSSLSocketFactory(); sf.setTrustAllHosts(true); props.put("mail.smtp.ssl.enable", "true"); //props.put("mail.smtp.ssl.checkserveridentity", "true"); props.put("mail.smtp.ssl.socketFactory", sf); } catch (GeneralSecurityException e1) { logger.error("開始SSL設置出錯", e1); } } props.setProperty("mail.smtp.auth", "true");//指定是否需要SMTP驗證 props.setProperty("mail.smtp.host", smtpHost);//指定SMTP服務器 props.setProperty("mail.smtp.port", smtpport);//指定SMTP服務器端口 props.put("mail.transport.protocol", "smtp"); Session mailSession = Session.getInstance(props); mailSession.setDebug(true);//是否在控制台顯示debug信息 //3、構建發送郵件的具體消息體 logger.debug("Constructing message - from=" + from + " to=" + to); String nick = javax.mail.internet.MimeUtility.encodeText("郵件用戶昵稱,根據自己實際定義"); //設置發送人 InternetAddress fromAddress = new InternetAddress(nick+" <"+from+">"); //設置接收人 InternetAddress toAddress; toAddress = new InternetAddress(to); MimeMessage testMessage = new MimeMessage(mailSession); try { testMessage.setFrom(fromAddress); } catch (MessagingException e) { logger.error(e); return 535; } try { testMessage.addRecipient(javax.mail.Message.RecipientType.TO, toAddress); } catch (MessagingException e) { logger.error(e); return 534; } testMessage.setSentDate(new java.util.Date()); testMessage.setSubject(MimeUtility.encodeText(subject,"gb2312","B")); //設置消息文本 testMessage.setContent(messageText, "text/html;charset=gb2312"); System.out.println("Message constructed"); Transport transport; transport = mailSession.getTransport("smtp"); //設置郵箱服務器 用戶名 密碼 transport.connect(smtpHost, from, password); transport.sendMessage(testMessage, testMessage.getAllRecipients()); transport.close(); return 200; }catch(SMTPSendFailedException e){ logger.error(e); return 550; }catch (MessagingException e) { logger.error(e); return 500; } catch (UnsupportedEncodingException e) { logger.error(e); return 501;//文本消息有錯 } } }
本文是用的騰訊企業郵箱作為測試的,但是發現在測試過程中,很容易造成:
javax.mail.AuthenticationFailedException: 535 Error: authentication failed, system busy
用於登錄失敗,所以整理了下具體原因:
1:郵箱有獨立密碼,非郵箱登錄密碼
2:郵箱設置的客戶端單獨授權碼
進入郵箱,點擊設置:
額,第一次寫文,話不多說,直接都是給的精華,希望初學者能理解到。
另外代碼封裝不是很好,主要是為了測試,請在自己項目中重新封裝下,以便於程序的擴展。