How to send Emails using Java and SMTP: A comprehensive guide

How to Send Emails in Java: A Complete Guide with Code Examples | UniOne
Alexey Kachalov Alexey Kachalov 09 april 2025, 09:38 101
For experts

Building on my previous tutorial about sending emails with PHPMailer, this guide explores the nuances of email delivery using Java and SMTP.

We will learn how to send emails using Java and choose the proper library and SMTP service for your email implementations. We will look at how to send emails using the following methods and libraries:

  • Jakarta email
  • Java Spring Mail
  • Apache Common Mail
  • Simple Java Mail
  • Spring Mail + UniOne SMTP

Without wasting much time, let’s gear up and get right in!

SMTP vs. ESP-provided APIs: Differences, Benefits, and Drawbacks

When sending emails from applications or websites, developers typically choose between two methods: traditional SMTP and modern Email APIs from Email Service Providers. While both are used to deliver emails, they are different in how they are implemented and are aimed at different use cases. Let's take the time to understand their differences, benefits, and drawbacks in the coming subsections.

Understanding SMTP

SMTP has been the foundation of email communication since the 1980s, operating through a direct connection between sender (an email client app or an intermediate SMTP server) and recipient (always an SMTP server, either final destination or intermediate) with a structured command-response workflow. Its widespread adoption makes it relatively easy to set up with minimal technical expertise.

SMTP's greatest strengths include platform independence (working consistently across different systems) and straightforward troubleshooting due to the fact that the protocol commands are human-readable.

However, using a plain SMTP connection in your app doesn’t work very well for bulk emails due to its sequential process, which can create performance issues. If you need advanced features like mass sending, variable substitution, or delivery status tracking, you’ll have to implement them by yourself. 

The Rise of Email APIs

Email APIs came in as a modern solution to email delivery, allowing applications to send emails using standard HTTP API requests. Email APIs are usually offered by Email Service Providers (ESPs) such as Mailchimp, SendGrid, or UniOne.

This method offers notably faster delivery for bulk emails by handling multiple requests in parallel without establishing new connections for each email. API methods provide easy template management, personalisation and message manipulation. You also get better security through API keys for authentication, providing protection beyond basic username/password combinations. Besides, ESPs completely remove the headaches of managing your own email infrastructure.

Most ESP APIs also offer comprehensive email analytics functions, allowing developers to track important metrics like delivery, open, and click-through rates and receive event notifications in real time.

The flexibility of Email APIs allows for sophisticated workflows, flexible content customization, and easy integration with other services. However, implementation requires programming knowledge and creates a dependency on third-party services, which may experience disruptions or updates affecting your application.

How do you make the right choice?

Well, this greatly depends on your use case. SMTP remains excellent for straightforward setups without extensive development needs. Organizations with minimal email requirements or those needing platform independence across diverse systems often find SMTP the perfect choice.

Email APIs excel at large-scale operations and complex flows but require more technical expertise to set up, as mentioned earlier.

Luckily, some providers offer both Email API and SMTP Service options, allowing you to select the best method for your project's requirements.

Main Java Email Libraries for sending emails via Java

This section will cover the different Java email libraries highlighted in the introduction, discussing their distinct features and benefits, and basic usage. Note that the code samples provided relate to the plain SMTP connection option described above.

Sending emails using Jakarta Mail (Formerly JavaMail)

Jakarta Mail is a comprehensive Java framework that allows you to send emails via SMTP or receive them via IMAP or POP. You’ll find it helpful if you run enterprise applications that require advanced email capabilities.

Main features

  • Integration with Jakarta EE ecosystem
  • Support for various message types (plain text, HTML, multipart, etc.)
  • STARTTLS and implicit SSL/TLS support
  • Attachment handling
  • Asynchronous sending
  • OAuth2 support

Setting up Jakarta Mail

There are multiple options for new users to set up Jakarta Mail. Let’s examine them:

1. Download the latest version from the official GitHub Page

Note: The latest version is 2.1.3 as of the time of writing; you may need to view the changelog for a summary of new features in later versions.

2. Use dependency managers like Maven or Gradle (recommended):

  • Maven

Get started by creating a folder, cd into it, and run the command below to generate a pom.xml file:

mvn archetype:generate

Once the installation is concluded, you get this build success message on your terminal:

Jakarta Mail Setting Terminal Build Success Message

Now, in the pom.xml file, you'll find the dependencies below automatically generated for you:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.unione</groupId>
  <artifactId>java-email-mvn</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>java-email-mvn</name>
  <!-- FIXME change it to the project's website -->
  <url>https://unione.io/</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.release>17</maven.compiler.release>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.junit</groupId>
        <artifactId>junit-bom</artifactId>
        <version>5.11.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <!-- Optionally: parameterized tests support -->
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  • Gradle

Get started by creating a folder, cd into it, and run the command below to generate a basic project structure:

gradle init

Once the installation is completed, you get this build success message on your terminal:

Jakarta Mail Installation Terminal Build Success Message

When you open the file in your IDE, you’ll see the project structure. Navigate to the build.gradle file and paste in the code below:

plugins {
    id 'java'
    id 'application'
}

group = 'org.unione'
version = '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

application {
    mainClass = 'Main' // Replace 'Main' with your main class name
}

dependencies {
    implementation 'org.eclipse.angus:angus-mail:2.0.3' // Latest version of Angus Mail
}

Sending an email using Jakarta

Now that we have the installation complete, it’s time for your favorite part: sending out emails :). To do this, navigate to the App.java file, as shown below:

App Java file navigation

Next, paste in the code below:

package org.unione;

import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;

import java.util.Properties;

public class JakartaMailExample {
    public static void main(String[] args) {
        // Recipient and sender details
        String to = "sender_name@sender.com";
        String from = "addressee_name@target.com";
     
        // SMTP server credentials
        final String username = "your_username"; //user_id from unione dashboard
        final String password = "your_password"; //For UniOne, use the API key from your account
        String host = "smtp.yourmailserver.com"; //example: smtp.us1.unione.io
     
        // Configure SMTP properties
        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.smtp.host", host);
        props.put("mail.smtp.port", "587");
     
        // Create a mail session with authentication
        Session session = Session.getInstance(props,
            new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(username, password);
                }
            });
     
        try {
            // Create a message
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress(from));
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(to));
            message.setSubject("Test Email from Jakarta Mail");
            message.setText("Hello! This is a test email sent using Jakarta Mail.");
         
            // Send the message
            Transport.send(message);
         
            System.out.println("Email sent successfully!");
         
        } catch (MessagingException e) {
            throw new RuntimeException("Failed to send email", e);
        }
    }
}

For our email host, you can either use UniOne (as shown in the code comment) or any other email hosting provider of your choice.

You can run the code using the Main.java command. Alternatively, use the following commands if you installed Maven or Gradle:

# Maven
mvn clean compile
mvn exec:java

# Gradle on MacOS/Linux
./gradlew clean build
./gradlew run

# Gradle on Windows
.\gradlew.bat clean build
.\gradlew.bat run

Sending emails using Java Spring Mail

Spring Mail is a module within the Spring Framework that provides a higher-level abstraction over Jakarta Mail. It's ideal for Spring-based applications and offers easy integration with other Spring components.

Main features

  • Integration with Spring Boot
  • Support for template engines (Thymeleaf, Freemarker, etc.)
  • Simplified configuration via application properties
  • Asynchronous email sending
  • Exception handling through Spring's exception hierarchy

Setting up Spring Mail

To get started, create a new folder in your local machine, cd into it, open it on your command line, and run the command spring init. Add the spring-boot-starter-mail  when running the command:

spring init -d=mail --package-name=org.unione <directory>

The current Spring Boot version is 3.4.4 and this may be subject to change in the future.

Note: Another approach is using the Spring Initializr web tool to generate your project structure and include relevant dependencies, such as the Java Email Sender library.

Sending plain texts using Spring Mail

To send plain-text emails, navigate to the main application file and rename the file to match your use case (I named mine EmailSending.java), then paste in the code below:

package org.mailtrap.sendemail;

import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class EmailService {

    private final JavaMailSender mailSender;

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

    @Async
    public void sendEmail(String to, String subject, String body) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(to);
        message.setFrom("sender@yourdomain.com");
        message.setSubject(subject);
        message.setText(body);
       
        mailSender.send(message);
        System.out.println("Email sent to: " + to);
    }
}

Next, navigate to  application.properties file and add the code:

spring.mail.host=smtp.us1.unione.io
spring.mail.port=587
spring.mail.username=user_id
spring.mail.password=api_key
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

Running your Spring Email Application

To run your application, update your code:

package org.unione.sendemail;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
public class SendEmailApplication {

    private final EmailService emailService;

    public SendEmailApplication(EmailService emailService) {
        this.emailService = emailService;
    }

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(SendEmailApplication.class, args);
        SendEmailApplication app = context.getBean(SendEmailApplication.class);

        app.run();
    }

    private void run() {
        emailService.sendEmail(
            "your_email@example.com",
            "Email From JavaSpringBoot",
            "Hi There, this is an email from JavaSpringBoot"
        );
    }
}

This code above bootstraps the Spring Boot application and calls the run method to execute the email-sending functionality. Now, run your app using mvn spring-boot:run command.

Sending emails using Apache Commons Email

Apache Commons Email provides a simplified programming interface built on top of Jakarta Mail, making it easier to use for basic email tasks. It also serves as a foundation for other email-sending tools. Developers have leveraged it to create things like mailR (for sending emails in R) and email components for the Play Framework. 

Main features

  • Simpler API compared to Jakarta Mail
  • Support for HTML emails
  • Attachment handling
  • Embedded image support
  • Multipart message support

Setting up Apache Commons Email

According to this Apache Commons user guide, you must choose between Javax org.apache.commons:commons-email2-javax , or Jakarta org.apache.commons:commons-email2-jakarta implementation. Anyone you choose installs the org.apache.commons:commons-email2-core module. Installation is done using Maven.

Adding Dependencies

In your Maven project, locate the pom.xml file and add the dependency:

# Javax

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-email2-javax</artifactId>
    <version>2.0.0-M1</version>
</dependency>

# Jakarta

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-email2-jakarta</artifactId>
    <version>2.0.0-M1</version>
</dependency>

The latest Apache Commons Email version is 2.0.0-M1 and this may be subject to change in the future.

Adding email with SMTP details to the main application file

To send a plain text email, use the code below:

Package app;

import org.apache.commons.mail.DefaultAuthenticator;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.SimpleEmail;

public class ApacheCommonsEmailExample {
    public static void main(String[] args) {
        try {
            SimpleEmail email = new SimpleEmail();
            email.setHostName("smtp.us1.unione.io");
            email.setSmtpPort(587);
            email.setAuthenticator(new DefaultAuthenticator("your_user_id", "your_api_key")); // Replace "your_user_id" and "your_api_key" with your user ID and API key, respectively
            email.setSSLOnConnect(true);
           
            email.setFrom("sender@yourdomain.com", "Sender Name");
            email.addTo("recipient@example.com", "Recipient Name");
            email.setSubject("Test Email from Apache Commons Email");
            email.setMsg("Hello! This is a test email sent using Apache Commons Email.");
           
            email.send();
            System.out.println("Email sent successfully!");
           
        } catch (EmailException e) {
            e.printStackTrace();
        }
    }
}

To send an HTML email, change setMsg() to setHtmlMsg().

Sending emails using Simple Java Mail

This library lives up to its name. It is simple to use and better suited for small projects.

Main features

  • Extremely clean and fluent API
  • Support for HTML emails with inline images
  • Attachment handling
  • DKIM signing
  • Support for multiple recipients
  • SSL/TLS encryption

Setting up Simple Java Mail

To set up Simple Java Mail, add the dependency below to your project:

Maven

#pom.xml


<dependency>
    <groupId>org.simplejavamail</groupId>
    <artifactId>simple-java-mail</artifactId>
    <version>8.12.2</version>
</dependency>

Gradle

implementation 'org.simplejavamail:simple-java-mail:8.12.2'

Sending a basic email

To send a basic email, navigate to your Main.java file and paste in the code:

import org.simplejavamail.api.email.Email;
import org.simplejavamail.api.mailer.Mailer;
import org.simplejavamail.email.EmailBuilder;
import org.simplejavamail.mailer.MailerBuilder;
import org.simplejavamail.transport.TransportStrategy;

public class SimpleJavaMailExample {
    public static void main(String[] args) {
        // Create the email
        Email email = EmailBuilder.startingBlank()
            .from("Sender Name", "sender@yourdomain.com")
            .to("Recipient Name", "recipient@example.com")
            .withSubject("Test Email from Simple Java Mail")
            .withPlainText("Hello! This is a test email sent using Simple Java Mail.")
            .buildEmail();

        // Configure and create the mailer
        Mailer mailer = MailerBuilder
            .withSMTPServer("smtp.us1.unione.io", 587, "your_user_id", "your_api_key")
            .withTransportStrategy(TransportStrategy.SMTP_TLS)
            .buildMailer();

        // Send the email
        mailer.sendMail(email);
        System.out.println("Email sent successfully!");
    }
}

To send emails in HTML, change the .withPlainText to .withHTMLText.

Comparison and Use Cases

Here's a quick comparison table for the libraries described above: 

Library

Complexity

Best For

Jakarta Mail

High

Enterprise applications requiring advanced email features

Spring Mail

Medium

Spring-based applications

Apache Commons Email

Low

Applications requiring basic email functionality

Simple Java Mail

Very Low

Quick implementation with minimal code

How to configure and set up email sending in Java using SMTP

Let's look at how to configure and set up email functionality using Spring Mail as a Java backend and UniOne as an email service provider. I chose this library due to its ease of use and compatibility with Spring Boot.

Prerequisites

  • JDK 8 or higher
  • Maven or Gradle for dependency management
  • SMTP server access credentials (see below)
  • Spring Boot (recommended for easier configuration)
  • UniOne account 

Setting up a UniOne Account

To set up a UniOne account, follow the steps below:

1. Create a free UniOne account. This is what your account dashboard looks like after signing up:

Java Send Email for Free with UniOne

2. Verify your sender domain. Learn how to do this in this video

3. Next, get your SMTP credentials. In UniOne, they include your username (user ID) and password (API key

Note: keep your API Key private, and DO NOT share it with anyone. 

You can find your user ID at the top left of your dashboard. For your password (API Key), navigate to “Account – Security” on the left navigation panel as shown below:

UniOne Account API Key

4. Use our dedicated SMTP debug tool to test out your connection.

Now that we have our SMTP credentials, it‘s time to set it up in our code.

Basic Java Configuration

For Spring Boot applications, configure your SMTP settings in application.properties or application.yml:

# properties


spring.mail.host=smtp.us1.unione.io
spring.mail.port=587
spring.mail.username=your-userid
spring.mail.password=your-apikey
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

If you're not using Spring Boot, you can configure JavaMailSender programmatically:

@Configuration
public class MailConfig {
   
    @Bean
    public JavaMailSender javaMailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost("smtp.us1.unione.io");
        mailSender.setPort(587);
        mailSender.setUsername("your-userid");
        mailSender.setPassword("your-apikey");
       
        Properties props = mailSender.getJavaMailProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable", "true");
        props.put("mail.debug", "true"); // Enable for debugging
       
        return mailSender;
    }
}

 

Sending Different Types of Emails

Now, let's explore how to send various types of emails using Spring Mail.

Sending Plain Text Emails

The simplest form of email contains only plain text:

@Service
public class EmailService {
   
    // Other code omitted for brevity
   
    public void sendTextEmail(String to, String subject, String text) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("noreply@yourdomain.com");
        message.setTo(to);
        message.setSubject(subject);
        message.setText(text);
        mailSender.send(message);
        logger.info("Plain text email sent to: {}", to);
    }
}

Sending HTML Emails

For more visually appealing emails, you can send HTML content:

public void sendHtmlEmail(String to, String subject, String htmlContent) throws MessagingException {
    MimeMessage message = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
   
    helper.setFrom("noreply@yourdomain.com");
    helper.setTo(to);
    helper.setSubject(subject);
    helper.setText(htmlContent, true); // true indicates HTML content
   
    mailSender.send(message);
    logger.info("HTML email sent to: {}", to);
}

Example HTML content:

String htmlContent = "<html>"
    + "<body>"
    + "<h1>Welcome!</h1>"
    + "<p>Thanks for registering with our service.</p>"
    + "<a href='https://yourservice.com/activate?token=xyz'>Activate your account</a>"
    + "</body>"
    + "</html>";

Sending Emails with Attachments

Adding attachments is common for sharing documents, reports, or images:

public void sendEmailWithAttachment(String to, String subject, String text,
                                  String attachmentPath, String attachmentName) throws MessagingException {
    MimeMessage message = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, true);
   
    helper.setFrom("noreply@yourdomain.com");
    helper.setTo(to);
    helper.setSubject(subject);
    helper.setText(text);
   
    FileSystemResource file = new FileSystemResource(new File(attachmentPath));
    helper.addAttachment(attachmentName, file);
   
    mailSender.send(message);
    logger.info("Email with attachment sent to: {}", to);
}

Let’s break down the code above a little. The code sends an email with an attachment by creating a MimeMessage and using a MimeMessageHelper to set the basic email fields (from, to, subject, and text content), then attaches a file by converting the physical file at the provided path into a FileSystemResource and adding it to the message with a specified name, before finally sending the email through the mailSender and logging the successful operation.

Sending Emails with Embedded Images

Embedding images directly in the email body can be useful for branding and rich content:

public void sendEmailWithEmbeddedImage(String to, String subject, String htmlContent,
                                    String imagePath, String imageContentId) throws MessagingException {
    MimeMessage message = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, true);
   
    helper.setFrom("noreply@yourdomain.com");
    helper.setTo(to);
    helper.setSubject(subject);
    helper.setText(htmlContent, true);
   
    FileSystemResource resource = new FileSystemResource(new File(imagePath));
    helper.addInline(imageContentId, resource);
   
    mailSender.send(message);
    logger.info("Email with embedded image sent to: {}", to);
}

Example usage:

String htmlContent = "<html><body>"
    + "<h1>Check out our new product:</h1>"
    + "<img src='cid:productImage'>"
    + "</body></html>";
   
sendEmailWithEmbeddedImage(
    "customer@example.com",
    "New Product Announcement",
    htmlContent,
    "path/to/product_image.jpg",
    "productImage"
);

Handling Multiple Recipients

There are several ways to send emails to multiple recipients using Spring Mail.

Sending to Multiple TO Recipients

Code implementation for sending to multiple TO recipients:

public void sendToMultipleRecipients(String[] to, String subject, String text) {
    SimpleMailMessage message = new SimpleMailMessage();
    message.setFrom("noreply@yourdomain.com");
    message.setTo(to);
    message.setSubject(subject);
    message.setText(text);
    mailSender.send(message);
    logger.info("Email sent to {} recipients", to.length);
}

Using CC and BCC Recipients

Code implementation for sending to cc and bcc recipients:

public void sendWithCcAndBcc(String to, String[] cc, String[] bcc, String subject, String text)
        throws MessagingException {
    MimeMessage message = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, false);
   
    helper.setFrom("noreply@yourdomain.com");
    helper.setTo(to);
    helper.setCc(cc);
    helper.setBcc(bcc);
    helper.setSubject(subject);
    helper.setText(text);
   
    mailSender.send(message);
    logger.info("Email sent with CC and BCC recipients");
}

Using Address Objects for Complex Scenarios

This code works well in complex scenarios:

public void sendWithPersonalNames(String to, String toPersonal,
                                String from, String fromPersonal,
                                String subject, String text) throws MessagingException, UnsupportedEncodingException {
    MimeMessage message = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(message, false, "UTF-8");
   
    helper.setFrom(new InternetAddress(from, fromPersonal));
    helper.setTo(new InternetAddress(to, toPersonal));
    helper.setSubject(subject);
    helper.setText(text);
   
    mailSender.send(message);
    logger.info("Email sent with personal names");
}

Implementing Bulk Email Sending

When sending emails in bulk, it's important to consider performance, resource usage, and deliverability.

Batch Sending with Spring Mail

Code implementation for sending batch emails with Spring Mail:

@Service
public class BulkEmailService {

    private final JavaMailSender mailSender;
    private static final int BATCH_SIZE = 100;
   
    @Autowired
    public BulkEmailService(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }
   
    public void sendBulkEmails(List<EmailDetails> emailsList) {
        int totalEmails = emailsList.size();
        int batches = (int) Math.ceil((double) totalEmails / BATCH_SIZE);
       
        for (int i = 0; i < batches; i++) {
            int start = i * BATCH_SIZE;
            int end = Math.min(start + BATCH_SIZE, totalEmails);
            List<EmailDetails> batch = emailsList.subList(start, end);
           
            processBatch(batch);
           
            // Pause between batches to avoid overwhelming the server
            if (i < batches - 1) {
                try {
                    Thread.sleep(1000);  // 1-second pause between batches
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
   
    private void processBatch(List<EmailDetails> batch) {
        for (EmailDetails email : batch) {
            try {
                MimeMessage message = mailSender.createMimeMessage();
                MimeMessageHelper helper = new MimeMessageHelper(message, true);
               
                helper.setFrom(email.getFrom());
                helper.setTo(email.getTo());
                helper.setSubject(email.getSubject());
                helper.setText(email.getContent(), email.isHtml());
               
                // Add attachments if any
                if (email.getAttachments() != null) {
                    for (Attachment attachment : email.getAttachments()) {
                        helper.addAttachment(attachment.getName(), attachment.getFile());
                    }
                }
               
                mailSender.send(message);
               
            } catch (MessagingException e) {
                logger.error("Failed to send email to {}", email.getTo());
            }
        }
    }
   
    // Supporting classes (implementation details omitted)
    @Data
    public static class EmailDetails {
        private String from;
        private String to;
        private String subject;
        private String content;
        private boolean isHtml;
        private List<Attachment> attachments;
    }
   
    @Data
    public static class Attachment {
        private String name;
        private FileSystemResource file;
    }
}

The BulkEmailService class is a Spring service designed for sending large volumes of emails in batches. It divides the full list of emails into smaller batches of 100 emails each (defined by BATCH_SIZE), then processes each batch separately with a one-second pause between batches to prevent overwhelming the SMTP server. 

Best Practices for Bulk Sending

Here are some of the best practices I'd recommend for sending bulk emails:

  • Throttle your sending rate: Respect SMTP server limits to avoid getting flagged as a spammer.
  • Monitor bounces and invalid addresses: Maintain a clean recipient list to get better deliverability.
  • Use BCC for privacy: When sending the same content to multiple recipients, use Blind Carbon Copy to hide selected addressees.
  • Consider using an ESP API: For very large volumes, an Email Service Provider API might be more efficient. The good news is that UniOne offers a powerful Email API for more large-scale requirements.
  • Implement retry logic: ESPs usually handle temporary failures for you, but for permanent errors with SMTP code 5XX, you must implement retry strategies on your own, depending on the reason of failure.

Implementing Asynchronous Email Sending

Email operations can be time-consuming. To prevent them from blocking your application, implement asynchronous sending.

Using Spring's @Async Annotation

First, enable asynchronous processing in your Spring Boot application:

@Configuration
@EnableAsync
public class AsyncConfig {
   
    @Bean
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("EmailAsync-");
        executor.initialize();
        return executor;
    }
}

Then implement asynchronous email methods:

@Service
public class AsyncEmailService {
   
    private final JavaMailSender mailSender;
    private static final Logger logger = LoggerFactory.getLogger(AsyncEmailService.class);
   
    @Autowired
    public AsyncEmailService(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }
   
    @Async
    public CompletableFuture<Boolean> sendEmailAsync(String to, String subject, String text) {
        try {
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom("noreply@yourdomain.com");
            message.setTo(to);
            message.setSubject(subject);
            message.setText(text);
           
            mailSender.send(message);
            logger.info("Async email sent successfully to {}", to);
            return CompletableFuture.completedFuture(true);
        } catch (Exception e) {
            logger.error("Failed to send async email: {}", e.getMessage());
            return CompletableFuture.completedFuture(false);
        }
    }
   
    @Async
    public CompletableFuture<Boolean> sendHtmlEmailAsync(String to, String subject,
                                                      String htmlContent) {
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
           
            helper.setFrom("noreply@yourdomain.com");
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(htmlContent, true);
           
            mailSender.send(message);
            logger.info("Async HTML email sent successfully to {}", to);
            return CompletableFuture.completedFuture(true);
        } catch (Exception e) {
            logger.error("Failed to send async HTML email: {}", e.getMessage());
            return CompletableFuture.completedFuture(false);
        }
    }
}

Example usage:

@RestController
public class NotificationController {
   
    private final AsyncEmailService emailService;
   
    @Autowired
    public NotificationController(AsyncEmailService emailService) {
        this.emailService = emailService;
    }
   
    @PostMapping("/notify")
    public ResponseEntity<String> notifyUser(@RequestBody NotificationRequest request) {
        // Trigger email and return immediately
        emailService.sendEmailAsync(request.getEmail(), "Notification", request.getMessage());
        return ResponseEntity.ok("Notification queued for delivery");
    }
}

How to test email functionality in a development environment

Now you are done with your email implementation, how do you test your emails?

For basic testing, it is recommended to set up a local SMTP server. Use either a production grade server software like Exim or Postfix, or install a “fake” server like smtp4dev, SMTP Bucket or similar.

For more comprehensive testing, especially in CI/CD pipelines, consider using Email Testing services that provide virtual inboxes and detailed analytics.

How UniOne Can Help

UniOne offers solutions for Java developers that address many of the challenges discussed in this article. You’ll certainly be impressed with our dedicated Email API, which provides a single interface for all your email needs.

But if you prefer the SMTP approach, UniOne's SMTP Service is here for you!

This tutorial isn't exhaustive but offers an insight into what is possible with sending emails using Java. Consult the documentation for each of the libraries to learn more about their strengths.

Conclusion

Implementing email functionality in Java requires careful consideration of the available libraries, configuration options, and best practices. Since I have covered those in this tutorial, I am positive you'll be able to find the right one for your use case.

For instance, if you work with simple applications, Jakarta Mail or Spring Mail provides everything you need. For more complex scenarios, especially those involving high volumes or marketing emails, considering an ESP like UniOne can save development time and improve deliverability.

Whatever approach you choose, remember to focus on security, reliability, and maintainability.

Frequently Asked Questions

How can I handle email bounces and delivery failures?

If using SMTP directly, implement bounce handling via log analysis, however your options will be limited. If using an ESP like UniOne, use the built-in webhooks feature to monitor and react to delivery issues. 

Which Java email library should I choose for my project?

The best library depends on your requirements:

  • For Spring applications, Spring Mail is the obvious choice
  • For standalone applications, Jakarta Mail provides the most flexibility
  • For simple use cases, Apache Commons Email or Simple Java Mail can reduce boilerplate

How can I prevent my emails from being marked as spam?

Follow these best practices:

  • Authenticate your emails with SPF, DKIM, and DMARC
  • Use a consistent sender address
  • Ensure your HTML is well-formed
  • Include both HTML and plain text versions, maintain text-to-image ratio
  • Monitor bounce rates and complaints
Related Articles
Blog
For beginners
Email Formatting: Examples & Best Practices for Success
Formatting emails is an art, and there’s no single way to do it perfectly. There are guidelines to f
Valeriia Klymenko
06 august 2024, 10:2910 min
Blog
For beginners
What is a Transactional Email and What is It Used for?
Remember when you created an account on a website and received an email to confirm your registration
Alexey Kachalov
20 october 2022, 10:5417 min
Blog
For beginners
What Is an Interactive Email?
Create an interactive email. Make emails better.
Valeriia Klymenko
22 february 2022, 15:257 min