Отправляйте электронные письма с динамическим HTML-контентом, используя шаблонизатор FreeMarker
Вступление
В программировании события - это действия, которые обрабатываются программным обеспечением. События могут запускаться по-разному, например, когда пользователь нажимает кнопку или сама система может вызвать событие. Суть в том, что приложение должно отслеживать такие действия или конкретное событие, а затем реагировать на них и каким-то образом обрабатывать их.
В Spring Framework одним из центральных интерфейсов является ApplicationContext
, и он имеет возможность публиковать события для зарегистрированных слушателей. Spring Framework также предоставляет абстракцию для отправки электронных писем, которая отвечает за низкоуровневую обработку ресурсов, а Spring Boot имеет стартовый модуль и автоконфигурацию для него.
В этой статье мы создадим пример, который будет использовать возможность Spring публиковать и прослушивать события, отправлять электронное письмо с динамическим HTML-содержимым с помощью Apache FreeMarker Template Engine каждый раз, когда клиент бронирует столик в ресторане.
Зависимости
Spring Boot предоставляет стартовые модули для Java Mail Sender и FreeMarker, наш первый шаг - добавить их в наш gradle.build
или pom.xml
файл или использовать Spring Initializer для быстрой генерации проекта.
implementation 'org.springframework.boot:spring-boot-starter-freemarker' implementation 'org.springframework.boot:spring-boot-starter-mail'
Конфигурация
Поскольку мы используем spring-boot-starter-mail
, Spring Boot автоматически настроит JavaMailSender
bean и сделает его доступным для внедрения. Мы можем настроить его дальше, используя элементы конфигурации, доступные в пространстве имен spring.mail
.
spring.mail.username= #email which will be used to send emails from spring.mail.password= #password spring.mail.port=587 spring.mail.host=smtp.gmail.com spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true
В этом примере проекта я буду использовать Gmail в качестве сервера SMTP (Simple Mail Transfer Protocol), поэтому я настроил его, указав свое имя пользователя / пароль и данные сервера Gmail. При желании вы можете использовать Outlook.
Обратите внимание: если в вашем аккаунте Google включена двухфакторная аутентификация, вы получите ошибку аутентификации. В этом случае вам нужно будет сгенерировать пароль приложения и использовать его вместо обычного пароля в файле application.properties
, чтобы ваше приложение получило доступ к вашей учетной записи Gmail.
Весенние события
Цель нашего проекта - отправить электронное письмо с подтверждением и деталями бронирования клиенту, который сделал заказ. Таким образом, при каждом создании резервирования приложение должно будет прослушивать это событие, а затем отвечать на него, отправляя электронное письмо.
Давайте создадим простое ReservationCreated
событие. Object source
, который затем передается в super()
, является фактическим объектом, на котором произошло событие, в основном источником события.
Обратите внимание, что вам не нужно расширять ApplicationEvent
, вы также можете публиковать объекты как событие, за исключением того, что, когда публикуемый нами объект не является ApplicationEvent
, он будет автоматически заключен в PayloadApplicationEvent
к весне.
Теперь нам нужен слушатель, который будет отслеживать появление событий:
Позже мы реализуем некоторую логику в прослушивателе событий onReservationCreated
для обработки фактического события.
JavaMailSender и Apache FreeMarker
Spring Boot предоставит нам bean-компонент JavaMailSender
и bean-компонент Apache FreeMarker Configuration
, который инкапсулирует параметры конфигурации FreeMarker и используется в качестве службы загрузки шаблонов.
Давайте создадим EmailService
, где мы загрузим шаблон, подготовим и отправим электронное письмо. FreeMarker имеет processTemplateIntoString
метод, который обрабатывает указанный шаблон с данной моделью. Затем мы можем использовать setText(String text, boolean html)
для установки обработанного шаблона.
Я использовал beefree.io для загрузки HTML-шаблона электронной почты и поместил его в папку templates
в структуре проекта с именем reservation-confirmation.ftl
, а изображения, использованные в шаблоне, находятся в папке static/images
. В шаблоне мы можем ссылаться на модель, используя ${reservation.numberOfPeople}
или ${
reservation.name} в соответствующих местах.
Собираем все вместе
Имея EmailService
на месте, давайте теперь реализуем onRegistrationCreated
прослушиватель для использования EmailService
и отправки электронной почты.
Мы извлекаем reservation
, исходный объект, на котором произошло событие, создаем модель, на которую будет указана ссылка в шаблоне FreeMarker, упаковываем все это и отправляем электронное письмо.
Обратите внимание, что наш метод прослушивателя событий также имеет аннотацию @Async
, поскольку я хочу, чтобы обработка событий происходила в другом потоке. Помните, чтобы использовать @Async
, мы должны @EnableAsync
в классе @SpringBootApplication
.
Осталось только опубликовать мероприятие. Для этого мы вводим ApplicationEventPublisher
и публикуем ReservationCreatedEvent
на Reservation
исходном объекте после его сохранения в базе данных.
Давай проверим.
Если вы получили SunCertPathBuilderException: unable to find valid certification path to requested target
, обязательно отключите антивирус во время тестирования приложения. И не используйте случайный адрес электронной почты в запросе, поскольку он фактически отправит электронное письмо с вашего адреса электронной почты, указанного в файле application.properties
как адрес fromEmail
.
Бонусный совет
Еще одна замечательная вещь, которую предоставляет Spring, - это возможность привязать слушателя к транзакции, используя аннотацию @TransactionalEventListener(phase= TransactionPhase.AFTER_COMMIT)
вместо @EventListener
. Поскольку в нашем примере событие запускается путем сохранения данных резервирования в базе данных, если эта транзакция по какой-либо причине откатывается или неуспешна, мы не хотим публиковать событие. Подробнее о транзакциях весной читайте в моей предыдущей статье.
В этом сценарии мы можем привязать прослушиватель событий к конкретному TransactionPhase
и публиковать событие только при успешной фиксации. Ниже приведены дополнительные константы Enum, которые можно передать @TransactionalEventListener
AFTER_COMMIT
- запускать событие после успешной фиксации.AFTER_COMPLETION
- запускать событие после завершения транзакцииAFTER_ROLLBACK
- запускать событие, если транзакция откатиласьBEFORE_COMMIT
- запускать событие перед фиксацией транзакции.
Исходный код этого примера проекта доступен на GitHub.
Подпишитесь на информационный бюллетень, чтобы получать будущие статьи прямо на свой почтовый ящик.