Spring Boot Email Sending

Applications send email constantly: welcome messages, password reset links, order receipts, and invoice copies. Spring Boot simplifies the work through the mail starter and the JavaMailSender interface. A few properties and a short service class are enough to deliver messages through any SMTP server.

Every email in an application should serve a clear purpose and reach people who expect it, such as a customer who just placed an order.

The Courier Service Idea

Think of a courier company. A shop prepares a package, writes the address, and hands the package to the courier. The courier carries it through depots and delivers it to the door. Email follows the same path. The application writes the message and hands it to an SMTP server, which routes it to the recipient's mailbox.

 Spring Boot app --> SMTP server --> Internet mail network --> Recipient inbox
   (writes mail)    (the courier)      (depots)                  (front door)

Adding the Starter

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

The starter brings Jakarta Mail and registers a JavaMailSender bean when mail properties exist.

Configuring the SMTP Server

spring.mail.host=smtp.example.com
spring.mail.port=587
spring.mail.username=${MAIL_USER}
spring.mail.password=${MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

Port 587 with STARTTLS encrypts the connection. The username and password come from environment variables, so credentials never appear in source control.

Common Settings

PropertyPurpose
spring.mail.hostAddress of the SMTP server
spring.mail.portServer port, often 587 or 465
mail.smtp.authTurns login on
mail.smtp.starttls.enableUpgrades the connection to encrypted
mail.smtp.timeoutLimits waiting time in milliseconds

Sending a Plain Text Email

@Service
public class EmailService {

    private final JavaMailSender mailSender;

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

    public void sendText(String to, String subject, String body) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("no-reply@shop.example.com");
        message.setTo(to);
        message.setSubject(subject);
        message.setText(body);
        mailSender.send(message);
    }
}

Sending an HTML Email

HTML messages support headings, links, and colors. MimeMessageHelper builds them.

public void sendHtml(String to, String name) throws MessagingException {
    MimeMessage message = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

    helper.setFrom("no-reply@shop.example.com");
    helper.setTo(to);
    helper.setSubject("Welcome to Book Shop");
    helper.setText("<h2>Welcome, " + name + "</h2>"
            + "<p>Your account is ready.</p>", true);

    mailSender.send(message);
}

The true flag at the end of setText marks the content as HTML.

Adding an Attachment

helper.addAttachment("invoice.pdf", new FileSystemResource(new File("invoices/501.pdf")));

Using Templates

Writing HTML inside Java strings gets messy. Thymeleaf templates keep the design in a separate file.

Context context = new Context();
context.setVariable("name", name);
String html = templateEngine.process("welcome-email", context);
helper.setText(html, true);

The template file welcome-email.html lives in src/main/resources/templates and holds the layout with placeholders such as th:text for the name.

Sending Without Blocking

SMTP servers respond slowly. A user who clicks Register should not wait for the mail server. Move the sending work to a background thread with the @Async annotation.

@Async
public void sendWelcome(String to, String name) {
    // build and send the message
}
 User registers --> account saved --> response returned immediately
                         |
                         +--> background thread sends the email

Testing Safely

Development machines should never send real emails to real customers. A local fake mail server catches every message for inspection.

  • Run Mailpit or MailHog in Docker and point spring.mail.host to localhost.
  • Open the tool's web page to read each captured message.
  • Use a test profile that targets the fake server.

Handling Failures

Mail sending throws MailException when the server refuses the connection or rejects a message. Catch the exception, write a clear log line, and queue a retry for important messages such as password resets.

try {
    mailSender.send(message);
} catch (MailException e) {
    log.error("Email to {} failed", to, e);
}

Writing Good Messages

Recipients read the subject first, so write one that names the content, such as "Your order 501 is confirmed". Keep the body short, state the key fact in the first line, and place one clear link or button for the next step. Use the recipient's name when available. Include the company name and contact details so readers recognize the sender.

Best Practices

  • Send email only to people who signed up or placed an order, and include a clear way to unsubscribe from non-essential messages.
  • Keep credentials in environment variables or a secrets store.
  • Use a trusted sending service and set up SPF and DKIM records so messages avoid spam folders.
  • Write short subjects that describe the content honestly.
  • Avoid placing passwords in the email body. Send a reset link with a short expiry instead.

Reliable email strengthens the user experience. A small, well-tested mail service covers signup confirmations, receipts, and alerts across the whole application.