本文是《輕量級 Java Web 框架架構設計》的系列博文。html
在 Java 應用系統中爲了實現郵件發送與收取功能,每每都會選擇使用 JavaMail API。但該 API 涉及的內容比較繁瑣,概念與細節都比較多,好比:Session、Message、Address、Authenticator、Transport、Store、Folder 等這些類,要想使用 JavaMail API,首先就要知道這些類到底是幹什麼的。我並不想庖丁解牛,由於已經有太多的專家講得比我好了,你們可充分利用搜索引擎來獲取知識。java
若是您也和我同樣,都屬於實踐派,那麼強烈推薦您閱讀《使用 JavaMail 實現郵件發送與收取》,再加上您的聰明才智,我想 JavaMail 很快就能被您把玩在手。程序員
若是您也和我同樣,愛好框架設計,我敢打賭,您必定會想辦法將 JavaMail 作一個封裝,讓它更加好用,而不是裸漏在外面給開發人員搗騰。apache
好了,下文即是 Smart Mail 插件開發過程。設計模式
在開發前,我找了許多選型,包括:Apache Commons Email 與 Jodd Email,它們都是比較優秀的 JavaMail 封裝,從本質上講,它們都是類庫(Library),而不是框架(Framework)。然而 Smart Mail 插件也是 Library,只不過它更加輕量級,它能夠自由在 Smart 框架中使用,固然也能夠在其餘框架中使用。通過全面對比,最終我選擇了 Apache Commons Email,隨後要作的事情就是,再進行一次封裝。Java 就是這樣,封裝、封裝、再封裝。架構
Smart Mail 插件包括兩個功能:框架
先看看發送郵件如何實現吧。ide
發送郵件其實分爲兩種,一種是發送純文本郵件,另外一種是發送 HTML 郵件。我想這兩種郵件都會在平時的業務需求中出現,因此將其作了一個區分,仍是頗有必要的,沒必要殺雞用牛刀了,具體狀況靈活運用。我首先想到的是,很是有必要給這兩類發送方式進行一個抽象,而後經過兩個具體類進行實現。單元測試
第一步:建立一個抽象的 MailSender 類測試
public abstract class MailSender { private static final Logger logger = Logger.getLogger(MailSender.class); // 建立 Email 對象(在子類中實現) private final Email email = createEmail(); // 定義發送郵件的必填字段 private final String subject; private final String content; private final String[] to; public MailSender(String subject, String content, String[] to) { this.subject = subject; this.content = content; this.to = to; } public void addCc(String[] cc) { try { if (ArrayUtil.isNotEmpty(cc)) { for (String address : cc) { email.addCc(MailUtil.encodeAddress(address)); } } } catch (EmailException e) { logger.error("錯誤:添加 CC 出錯!", e); } } public void addBcc(String[] bcc) { try { if (ArrayUtil.isNotEmpty(bcc)) { for (String address : bcc) { email.addBcc(MailUtil.encodeAddress(address)); } } } catch (EmailException e) { logger.error("錯誤:添加 BCC 出錯!", e); } } public void addAttachment(String path) { try { if (email instanceof MultiPartEmail) { MultiPartEmail multiPartEmail = (MultiPartEmail) email; EmailAttachment emailAttachment = new EmailAttachment(); emailAttachment.setURL(new URL(path)); emailAttachment.setName(path.substring(path.lastIndexOf("/") + 1)); multiPartEmail.attach(emailAttachment); } } catch (MalformedURLException e) { logger.error("錯誤:建立 URL 出錯!", e); } catch (EmailException e) { logger.error("錯誤:添加附件出錯!", e); } } public final void send() { try { // 判斷協議名是否爲 smtp(暫時僅支持 smtp,將來可考慮擴展) if (!MailConstant.Sender.PROTOCOL.equalsIgnoreCase("smtp")) { logger.error("錯誤:不支持該協議!目前僅支持 smtp 協議"); return; } // 判斷是否支持 SSL 鏈接 if (MailConstant.Sender.IS_SSL) { email.setSSLOnConnect(true); } // 設置 主機名 與 端口號 email.setHostName(MailConstant.Sender.HOST); email.setSmtpPort(MailConstant.Sender.PORT); // 判斷是否進行身份認證 if (MailConstant.Sender.IS_AUTH) { email.setAuthentication(MailConstant.Sender.AUTH_USERNAME, MailConstant.Sender.AUTH_PASSWORD); } // 判斷是否開啓 Debug 模式 if (MailConstant.IS_DEBUG) { email.setDebug(true); } // 設置 From 地址 if (StringUtil.isNotEmpty(MailConstant.Sender.FROM)) { email.setFrom(MailUtil.encodeAddress(MailConstant.Sender.FROM)); } // 設置 To 地址 for (String address : to) { email.addTo(MailUtil.encodeAddress(address)); } // 設置主題 email.setSubject(subject); // 設置內容(在子類中實現) setContent(email, content); // 發送郵件 email.send(); } catch (Exception e) { logger.error("錯誤:發送郵件出錯!", e); } } protected abstract Email createEmail(); protected abstract void setContent(Email email, String content) throws MalformedURLException, EmailException; }
注意其中的 send 方法,它就是用來發送郵件的,發送郵件的具體步驟都寫在這個方法中。在構造器中初始化必備字段,除了提供幾個 addXxx 方法外(如 addCc、addBcc、addAttachment),還提供了兩個 abstract 方法,它們就是由子類實現的。這裏用到了什麼設計模式?——沒錯!正是模板方法模式(Template Method)。
第二步:提供兩種具體實現
如下是純文本郵件發送具體實現:
public class TextMailSender extends MailSender { public TextMailSender(String subject, String content, String[] to) { super(subject, content, to); } @Override protected Email createEmail() { return new MultiPartEmail(); } @Override protected void setContent(Email email, String content) throws MalformedURLException, EmailException { email.setMsg(content); } }
您沒有看錯,就這麼一點,由於最核心的邏輯都放在它的父類中了。但您知道,運行時真正起做用的不是父類,而是子類,這是什麼原理?——沒錯!多態。
如下是 HTML 郵件發送具體實現:
public class HtmlMailSender extends MailSender { public HtmlMailSender(String subject, String content, String[] to) { super(subject, content, to); } @Override protected Email createEmail() { return new ImageHtmlEmail(); } @Override protected void setContent(Email email, String content) throws MalformedURLException, EmailException { ImageHtmlEmail imageHtmlEmail = (ImageHtmlEmail) email; imageHtmlEmail.setDataSourceResolver(new DataSourceUrlResolver(new URL("http://"), true)); imageHtmlEmail.setHtmlMsg(content); } }
看起來與純文本郵件相似,只不過 HTML 郵件還支持在郵件內容中帶有圖片。
第三步:發送郵件測試
不妨經過 JUnit 單元測試對郵件發送進行驗證吧。
如下是發送純文本郵件:
public class SendTextMailTest { private static final String subject = "測試"; private static final String content = "歡迎使用 Smart Framework!"; private static final String[] to = {"jack<jack@xxx.com>"}; @Test public void sendTest() { MailSender mailSender = new TextMailSender(subject, content, to); mailSender.addAttachment("http://www.oschina.net/img/logo_s2.png"); mailSender.send(); System.out.println("發送完畢!"); } }
以上除了發送純文本的正文之外,還發送了一個圖片附件。
發送成功,如下是收到的郵件:
如下是發送 HTML 郵件:
public class SendHtmlMailTest { private static final String subject = "測試"; private static final String content = "" + "<p><a href='http://my.oschina.net/huangyong/blog/158380'>歡迎使用 Smart Framework!</a></p>" + "<p><a href='http://my.oschina.net/huangyong'><img src='http://static.oschina.net/uploads/user/111/223750_100.jpg'></a></p>"; private static final String[] to = {"jack<jack@xxx.com>"}; @Test public void sendTest() { MailSender mailSender = new HtmlMailSender(subject, content, to); mailSender.addAttachment("http://www.oschina.net/img/logo_s2.png"); mailSender.send(); } }
以上發送的郵件正文爲 HTML 格式,其中包括一個文字連接與圖片連接,一樣也包括了一個圖片附件。
發送成功,如下是收到的郵件:
總結
只需稍微對 Apache Commons Email 作一個封裝,即可輕易實現郵件發送功能,包括郵件附件與內嵌圖片等,這些看似複雜的技術都是易如反掌。若是使用 JavaMail API 或許程序員們會寫一大堆代碼,才能實現這類功能,運行確定沒問題,但美觀與簡潔程度確定不夠。
最後我想表達的是,Apache Commons Email 也並不是完美,它居然沒有對收取郵件進行一個優雅的封裝,或許做者認爲發送郵件的需求比較多吧,收取郵件不必作了。
那麼 Smart Mail 插件是如何簡單的實現收取郵件呢?下回分解!