原创

精通Spring Boot教程(19):使用thymeleaf或freemarker作为邮件模板

1. 概述

本篇文章中,将介绍如何使用Spring 模板引擎 ThymeleafFreeMarker ,来发送精美的HTML动态内容邮件。

2. Spring HTML 邮件

首先,我们向 EmailServiceImpl 类添加一个方法,以发送带有HTML正文的电子邮件:

private void sendHtmlMessage(String to, String subject, String htmlBody) throws MessagingException {
    MimeMessage message = emailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
    helper.setTo(to);
    helper.setSubject(subject);
    helper.setText(htmlBody, true);
    emailSender.send(message);
}

我们使用 MimeMessageHelper 填充消息。注意传递给 setText 方法的第二个参数为 true :它指定该邮件为HTML内容类型。

接下来看看如何使用 ThymeleafFreeMarker 模板来构建这个 htmlBody
thymeleaf&fremarker

3. Thymeleaf 配置

先从配置开始,我们可以新建一个配置类,取名为 EmailConfiguration 。然后,提供一个模板解析器来定位模板文件目录。

3.1. Classpath Resources 中获取模板

模板文件可以在JAR文件中提供, 这是保持模板与其输入数据之间内聚性的最简单方法。

为了从JAR中定位模板,我们使用 ClassLoaderTemplateResolver。模板位于main/resources/mail templates目录中,因此我们设置了相对于resource目录的 Prefix 属性:

@Bean
public ITemplateResolver thymeleafTemplateResolver() {
    ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
    templateResolver.setPrefix("mail-templates/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode("HTML");
    templateResolver.setCharacterEncoding("UTF-8");
    return templateResolver;
}

3.2. 外部目录获取模板

在某些情况下,我们希望修改模板但却不需要重新构建和部署。为了实现这一点,我们可以将模板放在文件系统上。
application.properties 中配置此路径非常明智,这样我们就可以方便地修改模板路径。可以使用 @Value 注解来访问此属性:

@Value("${spring.mail.templates.path}")
private String mailTemplatesPath;

然后,我们将此模板路径传递给 FileTemplateResolver ,以代替 ThymeleaftTemplateResolver 方法中的ClassLoaderTemplateResolver

FileTemplateResolver templateResolver = new FileTemplateResolver();
templateResolver.setPrefix(mailTemplatesPath);

3.3.配置 Thymeleaf 引擎

最后一步是为 Thymeleaf 引擎创建工厂方法。我们需要告诉引擎选择了哪个TemplateResolver,可以通过参数将其注入bean factory方法:

@Bean
public SpringTemplateEngine thymeleafTemplateEngine(ITemplateResolver templateResolver) {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(templateResolver);
    templateEngine.setTemplateEngineMessageSource(emailMessageSource());
    return templateEngine;
}

我们前面创建的解析器会通过 Spring 自动注入模板引擎工厂方法。

4. FreeMarker 配置

与 Thymeleaf 相同,在 EmailConfiguration 类中,我们将为 FreeMarker 模板 配置模板解析器:
而这次,模板的位置将配置在 FreeMarkerConfigurer bean 中。

4.1. Classpath中获取模板

在这里,我们与 Thymeleaf 相同,把模板配置为 classpath resources:

@Bean 
public FreeMarkerConfigurer freemarkerClassLoaderConfig() {
    Configuration configuration = new Configuration(Configuration.VERSION_2_3_27);
    TemplateLoader templateLoader = new ClassTemplateLoader(this.getClass(), "/mail-templates");
    configuration.setTemplateLoader(templateLoader);
    FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer();
    freeMarkerConfigurer.setConfiguration(configuration);
    return freeMarkerConfigurer; 
}

4.2. 文件系统中获取模板

要从文件系统中的路径配置模板,我们需要替换 TemplateLoader 实例:

TemplateLoader templateLoader = new FileTemplateLoader(new File(mailTemplatesPath));

5. Thymeleaf 和 FreeMarker本地化

为了使用 Thymeleaf 引擎负责翻译模板,我们可以为其指定一个 MessageSource 实例:

@Bean
public ResourceBundleMessageSource emailMessageSource() {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    messageSource.setBasename("mailMessages");
    return messageSource;
}
@Bean
public SpringTemplateEngine thymeleafTemplateEngine() {
   ...
   templateEngine.setTemplateEngineMessageSource(emailMessageSource());
   ...
}

然后,为支持的每个语言环境创建资源包:

src/main/resources/mailMessages_xx_YY.properties

FreeMarker 建议通过 复制模板 进行本地化,因此我们不必在那里配置消息源。

6. Thymeleaf 模板内容

接下来,我们看看 template-thymeleaf.html 文件:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    <p th:text="#{greetings(${recipientName})}"></p>
    <p th:text="${text}"></p>
    <p th:text="#{regards}"></p>
    <p>
      <em th:text="#{signature(${senderName})}"></em> <br />
    </p>
  </body>
</html>

可以看出,我们使用了 Thymeleaf 表达式,也就是说,对于变量,${...}表示变量,#{...}表示本地化字符串。
模板引擎配置如果正确,使用它非常简单:我们只需创建一个Context 对象,其中包含模板变量(在这里作为Map传递)。
然后,我们将它与模板名一起传递给process方法:

@Autowired
private SpringTemplateEngine thymeleafTemplateEngine;

@Override
public void sendMessageUsingThymeleafTemplate(
    String to, String subject, Map<String, Object> templateModel)
        throws MessagingException {

    Context thymeleafContext = new Context();
    thymeleafContext.setVariables(templateModel);
    String htmlBody = thymeleafTemplateEngine.process("template-thymeleaf.html", thymeleafContext);

    sendHtmlMessage(to, subject, htmlBody);
}

接下来,看看如何用 FreeMarker 做同样的事情。

7. FreeMarker 模板内容

通过下面的代码可以看出,FreeMarker 的语法更简单,但它同样不管理本地化的字符串。下面是英文版:

<!DOCTYPE html>
<html>
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    </head>
    <body>
      <p>Hi ${recipientName}</p>
      <p>${text}</p>
      <p>Regards,</p>
      <p>
        <em>${senderName} at 锅外的大佬</em> <br />
      </p>
    </body>
</html>

然后,我们应该使用 FreeMarkerConfigurer 类来获取模板文件,最后使用 FreeMarkerTemplateUtils 从 Map 中注入数据:

@Autowired
private FreeMarkerConfigurer freemarkerConfigurer;

@Override
public void sendMessageUsingFreemarkerTemplate(
    String to, String subject, Map<String, Object> templateModel)
        throws IOException, TemplateException, MessagingException {

    Template freemarkerTemplate = freemarkerConfigurer.getConfiguration()
      .getTemplate("template-freemarker.ftl");
    String htmlBody = FreeMarkerTemplateUtils.processTemplateIntoString(freemarkerTemplate, templateModel);

    sendHtmlMessage(to, subject, htmlBody);
}

为了能把邮件做的更加丰富,我们接下来了解一下如何在电子邮件签名中添加徽章或图片。
spring boot mail

8. 嵌入图像的电子邮件

在HTML电子邮件中包含图像是非常常见的,我们将了解如何使用[CID附件]来实现这一点 。通过将true传递给构造函数的第二个参数,将MimeMessageHelper设置为 multi-part

MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

然后将图像文件作为资源,可以使用 @Value 来实现:

@Value("classpath:/mail-logo.png")
Resource resourceFile;

mail-logo.png 文件位于 src/main/resources 目录中,回到 sendHtmlMessage 方法,我们将添加 resourceFile作为内联附件,以便能够用 CID 引用它:

helper.addInline("attachment.png", resourceFile);

最后使用 CID 符号在 Thymeleaf 和 FreeMarker 电子邮件模板中引用:

<img src="cid:attachment.png" />

9. 总结

在本文中,我们掌握了如何使用 Thymeleaf 和 FreeMarker 作为模板发送包含丰富的HTML内容邮件。大部分工作都与 Spring 有关;因此,对于发送电子邮件之类的简单需求,二者非常类似,使用其中一种方法就行。

正文到此结束