1. Overview
1.概述
In our previous article, we saw how to use Spring to write and send text emails.
在我们的前一篇文章中,我们看到了如何使用Spring来编写和发送文本邮件。
But it’s also possible to use Spring template engines to write beautiful HTML emails with dynamic content.
但也可以使用Spring模板引擎来编写具有动态内容的精美HTML电子邮件。
In this tutorial, we’re going to learn how to do it using the most famous of them: Thymeleaf and FreeMarker.
在本教程中,我们将学习如何使用其中最著名的方法。Thymeleaf和FreeMarker。
2. Spring HTML Emails
2.SpringHTML电子邮件
Let’s start from the Spring Email tutorial.
让我们从Spring Email 教程开始。
First, we’ll add a method to the EmailServiceImpl class to send emails with an HTML body:
首先,我们将为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);
}
We’re using MimeMessageHelper to populate the message. The important part is the true value passed to the setText method: it specifies the HTML content type.
我们正在使用MimeMessageHelper来填充消息。重要的部分是传递给setText方法的true值:它指定了HTML内容类型。
Let’s see now how to build this htmlBody using Thymeleaf and FreeMarker templates.
现在让我们看看如何使用Thymeleaf和FreeMarker模板构建这个htmlBody。
3. Thymeleaf Configuration
3.百里香配置
Let’s start with the configuration. We can isolate this in a class called EmailConfiguration.
让我们从配置开始。我们可以在一个叫做EmailConfiguration的类中把它隔离出来。
First, we should provide a template resolver to locate the template files directory.
首先,我们应该提供一个模板解析器来定位模板文件目录。
3.1. Templates as Classpath Resources
3.1.模板作为Classpath资源
Template files can be shipped within the JAR file, which is the simplest way to maintain cohesion between templates and their input data.
模板文件可以在JAR文件中运送,这是保持模板和其输入数据之间凝聚力的最简单方法。
To locate templates from the JAR, we use the ClassLoaderTemplateResolver. Our templates are in the main/resources/mail-templates directory, so we set the Prefix attribute relative to the resource directory:
为了从JAR中找到模板,我们使用ClassLoaderTemplateResolver。我们的模板在main/resources/mail-templates目录下,所以我们将Prefix属性设置为相对于resource目录。
@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. Templates From External Directory
3.2.来自外部目录的模板
In other cases, we may want to modify templates without having to rebuild and deploy. To achieve this, we can put the templates on the filesystem instead.
在其他情况下,我们可能希望修改模板,而不必重建和部署。为了达到这个目的,我们可以把模板放在文件系统上。
It might be useful to configure this path in application.properties so that we can modify it for each deployment. This property can be accessed using the @Value annotation:
在application.properties中配置这个路径可能是有用的,这样我们就可以为每次部署修改它。这个属性可以使用@Value注解来访问。
@Value("${spring.mail.templates.path}")
private String mailTemplatesPath;
We then pass this value to a FileTemplateResolver, in place of the ClassLoaderTemplateResolver in our thymeleafTemplateResolver method:
然后我们将这个值传递给FileTemplateResolver,以代替ClassLoaderTemplateResolver在我们的thymeleafTemplateResolver方法中。
FileTemplateResolver templateResolver = new FileTemplateResolver();
templateResolver.setPrefix(mailTemplatesPath);
3.3. Configure the Thymeleaf Engine
3.3.配置Thymeleaf引擎
The final step is to create the factory method for the Thymeleaf engine. We’ll need to tell the engine which TemplateResolver we’ve chosen, which we can inject via a parameter to the bean factory method:
最后一步是为Thymeleaf引擎创建工厂方法。我们需要告诉引擎我们选择了哪个TemplateResolver,我们可以通过一个参数注入到bean工厂方法。
@Bean
public SpringTemplateEngine thymeleafTemplateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
templateEngine.setTemplateEngineMessageSource(emailMessageSource());
return templateEngine;
}
Here, the resolver we created earlier is injected automatically by Spring into the template engine factory method.
这里,我们之前创建的解析器被Spring自动注入到模板引擎工厂方法中。
4. FreeMarker Configuration
4.FreeMarker配置
In the same fashion as Thymeleaf, in the EmailConfiguration class, we’ll configure the template resolver for FreeMarker templates (.ftl):
And this time, the location of the templates will be configured in the FreeMarkerConfigurer bean.
与Thymeleaf相同,在EmailConfiguration类中,我们将配置FreeMarker模板的模板解析器(.ftl):
而这一次,模板的位置将被配置在FreeMarkerConfigurerBean中。
4.1. Templates in the Classpath
4.1.Classpath中的模板
Here, we have the same options as for Thymeleaf. Let’s configure templates as classpath resources:
这里,我们有与Thymeleaf相同的选项。让我们把模板配置为classpath资源。
@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. Templates on the File System
4.2.文件系统中的模板
To configure templates from another path in the filesystem, we’ll need to replace the TemplateLoader instance:
要从文件系统的其他路径配置模板,我们需要替换TemplateLoader实例。
TemplateLoader templateLoader = new FileTemplateLoader(new File(mailTemplatesPath));
5. Localization with Thymeleaf and FreeMarker
5.用Thymeleaf和FreeMarker进行定位
In order to manage translations with Thymeleaf, we can specify a MessageSource instance to the engine:
为了用Thymeleaf管理翻译,我们可以指定一个MessageSource实例给引擎。
@Bean
public ResourceBundleMessageSource emailMessageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("mailMessages");
return messageSource;
}
@Bean
public SpringTemplateEngine thymeleafTemplateEngine() {
...
templateEngine.setTemplateEngineMessageSource(emailMessageSource());
...
}
Then, we’d create resource bundles for each locale we support:
然后,我们将为我们支持的每个地区创建资源包。
src/main/resources/mailMessages_xx_YY.properties
As FreeMarker proposes localization by duplicating the templates, we don’t have to configure the message source there.
由于FreeMarker通过复制模板提出本地化,我们不必在那里配置消息源。
6. Thymeleaf Templates Content
6.百里叶模板内容
Next, let’s have a look at the template-thymeleaf.html file:
接下来,让我们看一下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>
As can be seen, we’ve used Thymeleaf notation, that is, ${…} for variables and #{…} for localized strings.
可以看出,我们使用了Thymeleaf符号,即${…}表示变量,#{…}表示本地化的字符串。
As the template engine is correctly configured, it’s very simple to use it: We’ll just create a Context object that contains template variables (passed as a Map here).
由于模板引擎被正确配置,使用它非常简单。我们只需创建一个包含模板变量的Context对象(在此以Map形式传递)。
Then, we’ll pass it to the process method along with the template name:
然后,我们将它和模板名称一起传递给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);
}
Now, let’s see how to do the same thing with FreeMarker.
现在,让我们看看如何用FreeMarker做同样的事情。
7. FreeMarker Templates Content
7.FreeMarker模板内容
As can be seen, FreeMarker’s syntax is more simple, but again it does not manage localized strings. So, here’s the English version:
可以看出,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 Baeldung</em> <br />
</p>
</body>
</html>
Then, we should use the FreeMarkerConfigurer class to get the template file, and finally, FreeMarkerTemplateUtils to inject data from our Map:
然后,我们应该使用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);
}
To go further, we’ll see how to add a logo to our email signature.
为了更进一步,我们将看到如何在我们的电子邮件签名中添加一个标志。
8. Emails With Embedded Images
8.嵌入图片的电子邮件
Since it’s very common to include images in an HTML email, we’ll see how to do this using a CID attachment.
由于在HTML电子邮件中包含图片是非常常见的,我们将看到如何使用CID附件来实现这一点。
The first change concerns the sendHtmlMessage method. We have to set MimeMessageHelper as multi-part by passing true to the second argument of the constructor:
第一个变化是关于sendHtmlMessage方法。我们必须将MimeMessageHelper设置为多部分,在构造函数的第二个参数中传递true。
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
Then, we have to get the image file as a resource. We can use the @Value annotation for this:
然后,我们必须获得作为资源的图像文件。我们可以为此使用@Value注解。
@Value("classpath:/mail-logo.png")
Resource resourceFile;
Notice that the mail-logo.png file is in the src/main/resources directory.
注意,mail-logo.png文件在src/main/resources目录下。
Back to the sendHtmlMessage method, we’ll add resourceFile as an inline attachment, to be able to reference it with CID:
回到sendHtmlMessage方法,我们将添加resourceFile作为内联附件,以便能够用CID引用它。
helper.addInline("attachment.png", resourceFile);
Finally, the image has to be referenced from both Thymeleaf and FreeMarker emails using CID notation:
最后,图像必须使用CID符号从Thymeleaf和FreeMarker电子邮件中引用。
<img src="cid:attachment.png" />
9. Conclusion
9.结语
In this article, we’ve seen how to send Thymeleaf and FreeMarker emails, including rich HTML content.
在这篇文章中,我们已经看到了如何发送Thymeleaf和FreeMarker电子邮件,包括丰富的HTML内容。
To conclude, most of the work is related to Spring; therefore, the use of one or the other is quite similar for a simple need such as sending emails.
总而言之,大部分工作都与Spring有关;因此,对于发送电子邮件这样的简单需求来说,使用其中一个或另一个是非常相似的。
As always, the full source code of the examples can be found over on GitHub.
一如既往,可以在GitHub上找到这些例子的完整源代码。