В этом разделе описано, как отправлять электронную почту с помощью Spring Framework.

Библиотечные зависимости

Следующий JAR-файл должен быть в classpath вашего приложения, чтобы получить возможность использовать библиотеку электронной почты для Spring Framework:

Эта библиотека свободно доступна в Интернете – например, в Maven Central как com.sun.mail:jakarta.mail. Пожалуйста, убедитесь, что используете последнюю версию 1.6.x, а не Jakarta Mail 2.0 (которая поставляется с другим пространством имен пакетов).

Spring Framework предоставляет вспомогательную библиотеку для отправки электронной почты, которая ограждает от специфики базовой почтовой системы и отвечает за низкоуровневую обработку ресурсов в пространстве клиента.

Пакет org.springframework.mail – это пакет корневого уровня для средств поддержки электронной почты в Spring Framework. Центральным интерфейсом для отправки электронных писем является интерфейс MailSender. Простой объект-значение, который инкапсулирует свойства простого электронного письма, такие как from и to (плюс многие другие) – это класс SimpleMailMessage. Этот пакет также содержит иерархию проверяемых исключений, которые обеспечивают более высокий уровень абстракции поверх исключений почтовой системы более низкого уровня, при этом корневым исключением является MailException. См. соответствующий javadoc для получения дополнительной информации о полнофункциональной иерархии почтовых исключений.

Интерфейс org.springframework.mail.javamail.JavaMailSender привносит специализированные функции JavaMail, такие как поддержка MIME-сообщений, в интерфейс MailSender (от которого он наследуется). JavaMailSender также предусматривает интерфейс обратного вызова org.springframework.mail.javamail.MimeMessagePreparator для подготовки MimeMessage.

Использование

Предположим, что у нас есть бизнес-интерфейс под названием OrderManager, как показано в следующем примере:

public interface OrderManager {
    void placeOrder(Order order);
}

Далее предположим, что у нас есть требование, согласно которому необходимо создать сообщение электронной почты с номером заказа и отправить его клиенту, который разместил соответствующий заказ.

Базовое использование MailSender и SimpleMailMessage

В следующем примере показано, как использовать MailSender и SimpleMailMessage для отправки электронного письма при оформлении заказа:

import org.springframework.mail.MailException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
public class SimpleOrderManager implements OrderManager {
    private MailSender mailSender;
    private SimpleMailMessage templateMessage;
    public void setMailSender(MailSender mailSender) {
        this.mailSender = mailSender;
    }
    public void setTemplateMessage(SimpleMailMessage templateMessage) {
        this.templateMessage = templateMessage;
    }
    public void placeOrder(Order order) {
        // Проводим бизнес-расчеты...
        // Вызываем взаимодействующие объекты для сохранения заказа...
        /// Создаем безопасную для потока "копию" шаблонного сообщения и настраиваем ее
        SimpleMailMessage msg = new SimpleMailMessage(this.templateMessage);
        msg.setTo(order.getCustomer().getEmailAddress());
        msg.setText(
            "Dear " + order.getCustomer().getFirstName()
                + order.getCustomer().getLastName()
                + ", thank you for placing order. Your order number is "
                + order.getOrderNumber());
        try {
            this.mailSender.send(msg);
        }
        catch (MailException ex) {
            // просто заносим в журнал и двигаемся дальше...
            System.err.println(ex.getMessage());
        }
    }
}

В следующем примере показаны определения бинов для предыдущего кода:

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host" value="mail.mycompany.example"/>
</bean>
<!-- это шаблонное сообщение, которое можно предварительно загрузить с состоянием по умолчанию -->
<bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
    <property name="from" value="customerservice@mycompany.example"/>
    <property name="subject" value="Your order"/>
</bean>
<bean id="orderManager" class="com.mycompany.businessapp.support.SimpleOrderManager">
    <property name="mailSender" ref="mailSender"/>
    <property name="templateMessage" ref="templateMessage"/>
</bean>

Использование JavaMailSender и MimeMessagePreparator

В этом разделе описана другая реализация OrderManager, которая использует интерфейс обратного вызова MimeMessagePreparator. В следующем примере свойство mailSender имеет тип JavaMailSender, что позволяет использовать JavaMail-класс MimeMessage:

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessagePreparator;
public class SimpleOrderManager implements OrderManager {
    private JavaMailSender mailSender;
    public void setMailSender(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }
    public void placeOrder(final Order order) {
        // Проводим бизнес-расчеты...
        // Вызываем взаимодействующие объекты для сохранения заказа...
        MimeMessagePreparator preparator = new MimeMessagePreparator() {
            public void prepare(MimeMessage mimeMessage) throws Exception {
                mimeMessage.setRecipient(Message.RecipientType.TO,
                        new InternetAddress(order.getCustomer().getEmailAddress()));
                mimeMessage.setFrom(new InternetAddress("mail@mycompany.example"));
                mimeMessage.setText("Dear " + order.getCustomer().getFirstName() + " " +
                        order.getCustomer().getLastName() + ", thanks for your order. " +
                        "Your order number is " + order.getOrderNumber() + ".");
            }
        };
        try {
            this.mailSender.send(preparator);
        }
        catch (MailException ex) {
            // просто заносим в журнал и двигаемся дальше...
            System.err.println(ex.getMessage());
        }
    }
}
Код письма является сквозной функциональностью и вполне может быть кандидатом на рефакторинг в кастомный аспект Spring AOP, который затем может быть запущен в соответствующих точках соединения для цели OrderManager.

Средства поддержки почты в Spring Framework поставляются со стандартной реализацией интерфейса JavaMail. Для получения дополнительной информации см. соответствующий javadoc.

Использование MimeMessageHelper из JavaMail

Класс, который может пригодиться при работе с JavaMail-сообщениями, – это org.springframework.mail.javamail.MimeMessageHelper, который избавляет от необходимости использовать перегруженный API JavaMail. Используя MimeMessageHelper, довольно легко создать MimeMessage, как показано в следующем примере:

// безусловно, в любых случаях реального применения будет использоваться внедрение зависимостей
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");
MimeMessage message = sender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setTo("test@host.com");
helper.setText("Thank you for ordering!");
sender.send(message);

Отправка вложений и встроенных ресурсов

Многокомпонентные сообщения электронной почты позволяют использовать как вложения, так и встроенные ресурсы. Примеры встроенных ресурсов включают изображение или таблицу стилей, которые вы хотите использовать в своем сообщении, но не хотите, чтобы они отображались как вложение.

Вложения

В следующем примере показано, как использовать MimeMessageHelper для отправки электронного письма с одним вложением изображения в формате JPEG:

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");
MimeMessage message = sender.createMimeMessage();
// используем флаг true, чтобы указать, что вам требуется многокомпонентное сообщение
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("test@host.com");
helper.setText("Check out this image!");
// прикрепляем пресловутый файл Sample (на этот раз скопированный в c:/)
FileSystemResource file = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addAttachment("CoolImage.jpg", file);
sender.send(message);

Встроенные ресурсы

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

JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost("mail.host.com");
MimeMessage message = sender.createMimeMessage();
// используем флаг true, чтобы указать, что вам требуется многокомпонентное сообщение
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("test@host.com");
// используем флаг true, чтобы указать, что включенный текст является HTML
helper.setText("<html><body><img src='cid:identifier1234'></body></html>", true);
// давайте используем пресловутый файл Sample (на этот раз скопированный в c:/)
FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg"));
helper.addInline("identifier1234", res);
sender.send(message);
Встроенные ресурсы добавляются к MimeMessage с помощью указанного Content-ID (identifier1234 в приведенном выше примере). Порядок, в котором добавляется текст и ресурс, крайне важен. Обязательно добавляйте сначала текст, а затем ресурсы. Если сделать все наоборот, то ничего не сработает.

Создание содержимого электронной почты с помощью библиотеки шаблонов

Код в примерах, показанных в предыдущих разделах, явным образом создавал содержимое почтового сообщения, используя вызовы методов, таких как message.setText(..). Это нормально для простых случаев, и это нормально в контексте вышеупомянутых примеров, где целью было показать самые основы API-интерфейса.

Однако в типичном корпоративном приложении разработчики зачастую не создают содержимое почтовых сообщений с помощью показанного ранее подхода по ряду причин:

  • Создание почтового контента на основе HTML в коде Java является громоздким и чревато ошибками.

  • Нет четкого разделения между логикой вывода на экран и бизнес-логикой.

  • Изменение структуры отображения содержимого электронной почты требует написания Java-кода, перекомпиляции, повторного развертывания и так далее.

Обычно для решения этих проблем используется библиотека шаблонов (например, FreeMarker) для определения структуры вывода на экран содержимого электронной почты. Задачей кода остается лишь создание данных, которые будут визуализированы в шаблоне электронной почты, и отправка письма. Это определенно наиболее оптимальный метод, если содержание почтовых сообщений становится даже умеренно сложным, и с помощью вспомогательных классов FreeMarker в Spring Framework сделать все становится довольно просто.