初步實現 Mail 插件 —— 發送郵件

本文是《輕量級 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 EmailJodd Email,它們都是比較優秀的 JavaMail 封裝,從本質上講,它們都是類庫(Library),而不是框架(Framework)。然而 Smart Mail 插件也是 Library,只不過它更加輕量級,它能夠自由在 Smart 框架中使用,固然也能夠在其餘框架中使用。通過全面對比,最終我選擇了 Apache Commons Email,隨後要作的事情就是,再進行一次封裝。Java 就是這樣,封裝、封裝、再封裝。架構

Smart Mail 插件包括兩個功能:框架

  1. 發送郵件
  2. 收取郵件

先看看發送郵件如何實現吧。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 插件是如何簡單的實現收取郵件呢?下回分解!

相關文章
相關標籤/搜索