«1. Обзор

В нашей предыдущей статье мы видели, как использовать Spring для написания и отправки текстовых электронных писем.

Но также можно использовать механизмы шаблонов Spring для написания красивых электронных писем в формате HTML с динамическим содержимым.

В этом уроке мы узнаем, как это сделать, используя самые известные из них: Thymeleaf и FreeMarker.

2. Электронные письма в формате Spring HTML

Начнем с руководства по электронной почте Spring.

Во-первых, мы добавим в класс 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: оно определяет тип содержимого HTML.

Теперь посмотрим, как создать этот htmlBody с помощью шаблонов Thymeleaf и FreeMarker.

3. Настройка Thymeleaf

Начнем с настройки. Мы можем изолировать это в классе под названием EmailConfiguration.

Во-первых, мы должны предоставить распознаватель шаблонов для поиска каталога файлов шаблонов.

3.1. Шаблоны как ресурсы пути к классам

Файлы шаблонов могут быть отправлены в файле JAR, что является самым простым способом поддержания согласованности между шаблонами и их входными данными.

Чтобы найти шаблоны из JAR, мы используем ClassLoaderTemplateResolver. Наши шаблоны находятся в директории main/resources/mail-templates, поэтому мы устанавливаем атрибут 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 вместо ClassLoaderTemplateResolver в нашем методе thymeleafTemplateResolver:

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

3.3. Настройка движка Thymeleaf

Последний шаг — создание фабричного метода для движка Thymeleaf. Нам нужно сообщить движку, какой TemplateResolver мы выбрали, который мы можем внедрить через параметр в метод фабрики компонентов:

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

Здесь созданный ранее преобразователь автоматически внедряется Spring в механизм шаблонов. заводской метод.

4. Конфигурация FreeMarker

Так же, как Thymeleaf, в классе EmailConfiguration мы настроим преобразователь шаблонов для шаблонов FreeMarker (.ftl): И на этот раз расположение шаблонов будет настроено в bean-компонент FreeMarkerConfigurer.

4.1. Шаблоны в пути к классам

Здесь у нас те же параметры, что и для 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. Шаблоны в файловой системе

Чтобы настроить шаблоны из другого пути в файловой системе, нам нужно заменить экземпляр 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

<!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>

Далее давайте посмотрим на файл template-thymeleaf.html:

Как видно, мы использовали нотацию Thymeleaf, то есть ${â €¦} для переменных и #{…} для локализованных строк.

Поскольку механизм шаблонов настроен правильно, использовать его очень просто: мы просто создадим объект Context, который содержит переменные шаблона (здесь они передаются как карта).

@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

<!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>

Как видно, синтаксис FreeMarker проще, но опять же он не работает с локализованными строками. Итак, вот английская версия:

@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);
}

«

«Затем мы должны использовать класс FreeMarkerConfigurer для получения файла шаблона и, наконец, FreeMarkerTemplateUtils для вставки данных из нашей карты:

Чтобы идти дальше, мы увидим, как добавить логотип в нашу электронную подпись.

8. Электронные письма со встроенными изображениями

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

Так как очень часто в электронные письма в формате HTML добавляются изображения, мы посмотрим, как это сделать с помощью вложения CID.

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

Первое изменение касается метода sendHtmlMessage. Мы должны установить MimeMessageHelper как состоящую из нескольких частей, передав true второму аргументу конструктора:

Затем мы должны получить файл изображения в качестве ресурса. Для этого мы можем использовать аннотацию @Value:

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

Обратите внимание, что файл mail-logo.png находится в каталоге src/main/resources.

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

Возвращаясь к методу sendHtmlMessage, мы добавим resourceFile в качестве встроенного вложения, чтобы можно было ссылаться на него с помощью CID:

Наконец, на изображение нужно ссылаться как из электронных писем Thymeleaf, так и из электронных писем FreeMarker, используя CID обозначение:

9. Заключение

В этой статье мы увидели, как отправлять электронные письма Thymeleaf и FreeMarker, включая богатое содержимое HTML.