Course topics

By WebNest Studio

Spring Boot Tutorial

Sending Email with Spring Boot

Email is still the primary channel for account verification, password resets, receipts, notifications and newsletters. Spring Boot makes sending email straightforward with JavaMailSender, but production email also needs HTML templates, attachments, asynchronous sending, a safe local test setup, and good deliverability.

This lesson covers configuring SMTP (Gmail, Amazon SES, SendGrid, Mailgun), sending plain-text and HTML emails, rendering templates with Thymeleaf, adding attachments and inline images, testing locally with Mailpit, and practices that keep your emails out of spam folders.

Configuration

Add spring-boot-starter-mail and set spring.mail.host, port, username, password and the TLS properties. Spring Boot then auto-configures a JavaMailSender. For Gmail you must use an app password, not your account password; transactional email providers (Amazon SES, SendGrid, Mailgun, Postmark) are the better choice for production volume and deliverability.

Simple and MIME Messages

SimpleMailMessage covers plain-text emails. For HTML, attachments and inline images, create a MimeMessage and fill it with MimeMessageHelper in multipart mode. Always include a plain-text alternative alongside HTML; some clients and spam filters prefer it.

Templates

Building HTML in Java strings is unmaintainable. Use Thymeleaf's TemplateEngine to render templates/email/*.html with a Context of variables, then pass the rendered HTML to the helper. Email HTML should use simple table layouts and inline styles, because many clients ignore external CSS.

Asynchronous and Reliable Sending

SMTP calls take hundreds of milliseconds or more and can fail. Send from an @Async method or a message queue so user requests stay fast, send only after the database transaction commits (@TransactionalEventListener), and retry transient failures. For critical emails, record them in an outbox table and track delivery status.

Deliverability

Configure SPF, DKIM and DMARC DNS records for your sending domain, send from a consistent address on your own domain, include an unsubscribe link in marketing email, and never send from a no-reply@gmail.com-style address. Monitor bounces and complaints through your provider.

Examples

Configuration with Mailpit for local testing

Java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

# compose.yaml — Mailpit catches all mail locally (web UI on http://localhost:8025)
services:
  mailpit:
    image: axllent/mailpit
    ports:
      - "1025:1025"
      - "8025:8025"

# application.yml (development)
spring:
  mail:
    host: localhost
    port: 1025

# application-prod.yml (e.g. Amazon SES SMTP)
spring:
  mail:
    host: email-smtp.ap-south-1.amazonaws.com
    port: 587
    username: ${SMTP_USER}
    password: ${SMTP_PASSWORD}
    properties:
      mail.smtp.auth: true
      mail.smtp.starttls.enable: true
      mail.smtp.connectiontimeout: 5000
      mail.smtp.timeout: 5000
Output
(Every email sent in development appears in the Mailpit inbox at http://localhost:8025 — nothing reaches real users.)

Plain text and HTML emails with an attachment

Java
@Service
public class MailService {

    private final JavaMailSender mailSender;

    public MailService(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }

    public void sendPlain(String to, String subject, String text) {
        SimpleMailMessage msg = new SimpleMailMessage();
        msg.setFrom("Webnest Studio <hello@webneststudio.co.in>");
        msg.setTo(to);
        msg.setSubject(subject);
        msg.setText(text);
        mailSender.send(msg);
    }

    public void sendInvoice(String to, String orderId, String html, byte[] pdf) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");  // multipart
        helper.setFrom("Webnest Studio <billing@webneststudio.co.in>");
        helper.setTo(to);
        helper.setSubject("Your invoice for order " + orderId);
        helper.setText("Your invoice is attached.", html);          // plain-text + HTML alternatives
        helper.addInline("logo", new ClassPathResource("static/img/logo.png"));
        helper.addAttachment("invoice-" + orderId + ".pdf", new ByteArrayResource(pdf), "application/pdf");
        mailSender.send(message);
    }
}
Output
Mailpit inbox:
From: Webnest Studio <billing@webneststudio.co.in>
Subject: Your invoice for order WN-10231
Attachments: invoice-WN-10231.pdf (48 KB), inline logo.png

Thymeleaf email templates sent asynchronously after commit

Java
<!-- templates/email/welcome.html -->
<html xmlns:th="http://www.thymeleaf.org">
<body style="font-family: Arial, sans-serif">
  <table width="600" cellpadding="16">
    <tr><td>
      <h2 th:text="|Welcome, ${name}!|">Welcome!</h2>
      <p>Your first course is ready:</p>
      <a th:href="${courseUrl}" style="background:#d4a017;color:#000;padding:10px 16px;text-decoration:none">
        Start learning
      </a>
    </td></tr>
  </table>
</body>
</html>

@Component
public class WelcomeEmailListener {

    private final JavaMailSender mailSender;
    private final SpringTemplateEngine templates;

    public WelcomeEmailListener(JavaMailSender mailSender, SpringTemplateEngine templates) {
        this.mailSender = mailSender;
        this.templates = templates;
    }

    @Async
    @TransactionalEventListener
    public void onRegistered(UserRegisteredEvent e) throws MessagingException {
        Context ctx = new Context(Locale.ENGLISH);
        ctx.setVariable("name", e.name());
        ctx.setVariable("courseUrl", "https://www.webneststudio.co.in/learn/java-core");
        String html = templates.process("email/welcome", ctx);

        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
        helper.setTo(e.email());
        helper.setSubject("Welcome to Webnest Studio");
        helper.setText("Welcome, " + e.name() + "! Start learning: https://www.webneststudio.co.in/learn", html);
        mailSender.send(message);
    }
}
Output
POST /api/auth/register -> 201 in 38 ms
(after commit, on task-3) welcome email rendered and sent to asha@webnest.in

Common Mistakes

  • Sending email synchronously inside the request, making sign-up slow and failing it when SMTP is down.
  • Sending emails inside a transaction that may roll back, so users receive emails for actions that never happened.
  • Hard-coding SMTP passwords in application.yml.
  • Using a real SMTP server during development and accidentally emailing real customers; use Mailpit.
  • Sending HTML only without a plain-text part and without SPF/DKIM, hurting deliverability.

Key Points to Remember

  • spring-boot-starter-mail auto-configures JavaMailSender from spring.mail.* properties.
  • Use SimpleMailMessage for text; MimeMessageHelper for HTML, attachments and inline images.
  • Render HTML emails with Thymeleaf templates and include a plain-text alternative.
  • Send asynchronously and after commit; retry transient failures.
  • Test with Mailpit locally and configure SPF, DKIM and DMARC in production.

Practice the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

Use your local JDK or project IDE for these examples. Codelab currently runs Python and HTML/CSS/JavaScript; framework examples may need project dependencies.