Spring Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

In our previous article, we saw how to use Spring to write and send text emails.

But it's also possible to use Spring template engines to write beautiful HTML emails with dynamic content.

In this tutorial, we're going to learn how to do it using the most famous of them: Thymeleaf and FreeMarker.

2. Spring HTML Emails

Let's start from the Spring Email tutorial.

First, we'll add a method to the EmailServiceImpl class to send emails with an HTML body:

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 setText method: it specifies the HTML content type.

Let's see now how to build this htmlBody using Thymeleaf and FreeMarker templates.

3. Thymeleaf Configuration

Let's start with our Spring @Configuration annotated class. In the sample code, we'll isolate it in EmailConfiguration class.

First, we should provide the template resolver by specifying the template files directory:

@Bean
public SpringResourceTemplateResolver thymeleafTemplateResolver() {
    SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
    templateResolver.setPrefix("/WEB-INF/views/mail/");
    templateResolver.setSuffix(".html");
    templateResolver.setTemplateMode("HTML");
    templateResolver.setCharacterEncoding("UTF-8");
    return templateResolver;
}

Then, we have to provide the factory method for the Thymeleaf engine:

@Bean
public SpringTemplateEngine thymeleafTemplateEngine() {
    SpringTemplateEngine templateEngine = new SpringTemplateEngine();
    templateEngine.setTemplateResolver(thymeleafTemplateResolver());
    return templateEngine;
}

4. FreeMarker Configuration

In the same fashion as Thymeleaf, in the EmailConfiguration class, we'll configure the template resolver for FreeMarker templates (.ftl):

@Bean 
public FreeMarkerViewResolver freemarkerViewResolver() { 
    FreeMarkerViewResolver resolver = new FreeMarkerViewResolver(); 
    resolver.setSuffix(".ftl"); 
    return resolver; 
}

And this time, the templates path will be configured in the FreeMarkerConfigurer bean:

@Bean 
public FreeMarkerConfigurer freemarkerConfig() { 
    FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer(); 
    freeMarkerConfigurer.setTemplateLoaderPath("/WEB-INF/views/mail");
    return freeMarkerConfigurer; 
}

5. Localization with Thymeleaf and FreeMarker

In order to manage translations with Thymeleaf, we can specify a MessageSource instance to the engine:

@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 messages source there.

6. Thymeleaf Emails Templates

Next, let's have a look at the template-thymeleaf.html file:

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

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).

Then, we'll pass it to the process method along with the template name:

@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.

7. FreeMarker Emails Templates

As can be seen, FreeMarker's syntax is more simple, but again it does not manage localized strings. So here's the English version:

<!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 FreeMarkerConfigurer class to get the template file, and finally FreeMarkerTemplateUtils to inject data from our Map:

@Autowired
private FreeMarkerConfigurer freemarkerConfigurer;

@Override
public void sendMessageUsingFreemarkerTemplate(
    String to, String subject, Map<String, Object> templateModel)
        throws IOException, TemplateException, MessagingException {
        
    Template freemarkerTemplate = freemarkerConfigurer.createConfiguration()
      .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

Since it's very common to include images in an HTML email, we'll see how to do this using a CID attachment.

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:

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("classpath:/mail-logo.png")
Resource resourceFile;

Notice that the mail-logo.png file is in the src/main/resources directory.

Back to the sendHtmlMessage method, we'll add resourceFile as an inline attachment, to be able to reference it with CID:

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

Finally, the image has to be referenced from both Thymeleaf and FreeMarker emails using CID notation:

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

9. Conclusion

In this article, we've seen how to send Thymeleaf and FreeMarker emails, including rich HTML content.

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.

As always, the full source code of the examples can be found over on GitHub.

Spring bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!