1. 程式人生 > >第19章-使用Spring發送Email

第19章-使用Spring發送Email

pos resolve 上下文 這一 helper gte thymeleaf 復數 .com

1 配置Spring發送郵件

Spring Email抽象的核心是MailSender接口。顧名思義,MailSender的實現能夠通過連接Email服務器實現郵件發送的功能,如圖19.1所示。
技術分享圖片

圖19.1 Spring的MailSender接口是Spring Email抽象API的核心組件。它把Email發送給郵件服務器,由服務器進行郵件投遞

Spring自帶了一個MailSender的實現也就是JavaMailSenderImpl,它會使用JavaMail API來發送Email。Spring應用在發送Email之前,我們必須要將JavaMailSenderImpl裝配為Spring應用上下文中的一個bean。

1.1 配置郵件發送器

  @Bean
  public MailSender mailSender(Environment env) {
    JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
    mailSender.setHost(env.getProperty("mailserver.host"));
    mailSender.setPort(Integer.parseInt(env.getProperty("mailserver.port")));
    mailSender.setUsername(env.getProperty("mailserver.username"));
    mailSender.setPassword(env.getProperty("mailserver.password"));
    return mailSender;
  }

1.2 裝配和使用郵件發送器

我們想要給Spitter用戶發送Email提示他的朋友寫了新的Spittle,所以我們需要一個方法來發送Email,這個方法要接受Email地址和Spittle對象信息。

  @Override
  public void sendSimpleSpittleEmail(String to, Spittle spittle) {
    SimpleMailMessage message = new SimpleMailMessage();
    String spitterName = spittle.getSpitter().getFullName();
    message.setFrom("[email protected]");
    message.setTo(to);
    message.setSubject("New spittle from " + spitterName);
    message.setText(spitterName + " says: " + spittle.getText());
    mailSender.send(message);
  }

2 構建豐富內容的Email消息

Spring的Email功能並不局限於純文本的Email。我們可以添加附件,甚至可以使用HTML來美化消息體的內容。讓我們首先從基本的添加附件開始,然後更進一步,借助HTML使我們的Email消息更加美觀。

2.1 添加附件

如果發送帶有附件的Email,關鍵技巧是創建multipart類型的消息——Email由多個部分組成,其中一部分是Email體,其他部分是附件。

對於發送附件這樣的需求來說,SimpleMailMessage過於簡單了。為了發送multipart類型的Email,你需要創建一個MIME(Multipurpose Internet Mail Extensions)的消息,我們可以從郵件發送器的createMimeMessage()方法開始:
MimeMessage message = mailSender.createMimeMessage();

javax.mail.internet.MimeMessage本身的API有些笨重。好消息是,Spring提供的MimeMessageHelper可以幫助我們。為了使用MimeMessageHelper,我們需要實例化它並將MimeMessage傳給其構造器:
MimeMessageHelper helper = new MimeMessageHelper(message, true);
構造方法的第二個參數,在這裏是個布爾值true,表明這個消息是multipart類型的。

得到了MimeMessageHelper實例後,我們就可以組裝Email消息了。這裏最主要區別在於使用helper的方法來指定Email細節,而不再是設置消息對象.

  @Override
  public void sendSpittleEmailWithAttachment(String to, Spittle spittle) throws MessagingException {
    MimeMessage message = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, true);
    String spitterName = spittle.getSpitter().getFullName();
    helper.setFrom("[email protected]");
    helper.setTo(to);
    helper.setSubject("New spittle from " + spitterName);
    helper.setText(spitterName + " says: " + spittle.getText());
    ClassPathResource couponImage = new ClassPathResource("/collateral/coupon.png");
    helper.addAttachment("Coupon.png", couponImage);
    mailSender.send(message);
  }

2.2 發送富文本內容的Email

發送富文本的Email與發送簡單文本的Email並沒有太大區別。關鍵是將消息的文本設置為HTML。要做到這一點只需將HTML字符串傳遞給helper的setText()方法,並將第二個參數設置為true:
技術分享圖片

第二個參數表明傳遞進來的第一個參數是HTML,所以需要對消息的內容類型進行相應的設置。
要註意的是,傳遞進來的HTML包含了一個<img>標簽,用來在Email中展現Spittr應用程序的logo。src屬性可以設置為標準的“http:”URL,以便於從Web中獲取Spittr的logo。但在這裏,我們將logo圖片嵌入在了Email之中。值“cid:spitterLogo”表明在消息中會有一部分是圖片並以spitterLogo來進行標識。

為消息添加嵌入式的圖片與添加附件很類似。不過這次不再使用helper的addAttachment()方法,而是要調用addInline()方法:

ClassPathResource couponImage = new ClassPathResource("coupon.png");
helper.addInline("spitterLogo", couponImage);

以下是新的sendRichSpitterEmail()方法:
技術分享圖片

創建Email體時,使用字符串拼接的辦法來構建HTML消息依舊讓我覺得美中不足。在結束Email話題之前,讓我們看看如何用模板來代替字符串拼接消息。

3 使用模板生成Email

我們需要與最終HTML接近的方式來表達Email布局,然後將模板轉換成String並傳遞給helper的setText()方法。在將模板轉換為String時,我們有多種模板方案可供選擇,包括Apache Velocity和Thymeleaf。讓我們看一下如何使用這兩種方案創建富文本的Email消息,先從Velocity開始吧。

3.1 使用Velocity構建Email消息

為了使用Velocity對Email進行布局,我們需要將VelocityEngine裝配到SpitterEmailServiceImpl中。Spring提供了一個名為VelocityEngineFactoryBean的工廠bean,它能夠在Spring應用上下文中很便利地生成VelocityEngine。VelocityEngineFactoryBean的聲明如下:
技術分享圖片

VelocityEngineFactoryBean唯一要設置的屬性是velocityProperties。在本例中,我們將其配置為從類路徑下加載Velocity模板(關於配置Velocity的更多細節,請查閱Velocity文檔)。

現在,我們可以將Velocity引擎裝配到SpitterEmailServiceImpl中。因為SpitterEmailServiceImpl是使用組件掃描實現自動註冊的,我們可以使用@Autowired來自動裝配velocityEngine屬性:

@Autowired
VelocityEngine velocityEngine;

現在,velocityEngine屬性可用了,我們可以使用它將Velocity模板轉換為String,並作為Email文本進行發送。為了幫助我們完成這一點,Spring自帶了VelocityEngineUtils來簡化將Velocity模板與模型數據合並成String的工作。以下是我們可能的使用方式:
技術分享圖片

在Java代碼中剩下的事情就是得到合並後的Email文本,並將其傳遞給helper的setText()方法:

    helper.setText(emailText, true);

模板位於類路徑的根目錄下,是一個名為emailTemplate.vm的文件,它看起來可能是這樣的:
技術分享圖片

3.2 使用Thymeleaf構建Email消息

當我們將Email模板轉換為Thymeleaf模板時,Thymeleaf的WYSIWYG特性體現得非常明顯:
技術分享圖片

註意,這裏沒有任何自定義的標簽(在JSP中可能會見到這種情況)。盡管模型屬性是通過“${}”標記的,但是它們僅用於屬性的值中,不會像Velocity那樣用在外邊。這種模板可以很容易地在Web瀏覽器中打開,並且以完整的形式進行展現,不必依賴於Thymeleaf引擎的處理。

使用Thymeleaf來生成和發送Email消息的做法非常類似於Velocity:
技術分享圖片

這裏的Thymeleaf引擎與我們在第6章構建Web視圖時所使用的SpringTemplateEnginebean是相同的。在這裏,我們使用構造器註入的方式將其註入到SpitterEmailServiceImpl中:
技術分享圖片

不過,我們必要要對SpringTemplateEnginebean做一點小修改。在第6章中,它配置為從Servlet上下文中解析模板,而我們的Email模板需要從類路徑中解析。所以,除了ServletContextTemplateResolver,還需要一個ClassLoaderTemplateResolver:
技術分享圖片

需要註意,我們將prefix屬性設置為“mail/”,這表明它會在類路徑根的“mail”目錄下開始查找Thymeleaf模板。因此,Email模板文件的名字必須是emailTemplate.html,並且位於類路徑根的“mail”目錄下。

因為我們現在有兩個模板解析器,所以需要使用order屬性表明優先使用哪一個。ClassLoaderTemplateResolver的order屬性為1,因此我們修改一下ServletContextTemplateResolver,將其order屬性設置為2:
技術分享圖片

現在,剩下的任務就是修改SpringTemplateEnginebean的配置,讓它使用這兩個模板解析器:
技術分享圖片

在此之前,我們只有一個模板解析器,所以可以將其註入到SpringTemplateEngine的templateResolver屬性中。但現在我們有了兩個模板解析器,所以必須將它們作為Set的成員,然後將這個Set註入到templateResolvers(復數)屬性中。

源碼

https://github.com/myitroad/spring-in-action-4/tree/master/Chapter_19

附件列表

  • ClassLoaderTR.jpg
  • emailTemplate.jpg
  • img19-1.jpg
  • richText.jpg
  • sendRichSpitterEmail.jpg
  • TemplateEB.jpg
  • TemplateRO.jpg
  • ThymeleafEngine.jpg
  • ThymeleafSendEmail.jpg
  • ThymeleafTemplate.jpg
  • VelocityEFB.jpg
  • VelocityToStr.jpg

第19章-使用Spring發送Email