В рамках обучающей серии по Spring Boot в этой статье мы рассмотрим способ отправки электронных писем с помощью Spring.
Введение
Отправка электронных писем — одна из базовых функций, которая может понадобиться в любом приложении. Из этой статьи вы узнаете, как отправлять электронные письма с помощью Spring. Для отправки электронных писем вам потребуются данные SMTP-сервера. В качестве примера вы можете использовать SMTP для Gmail (имейте в виду, что у Gmail есть определенные ограничения по отправке электронной почты). Интерфейс JavaMailSender в Spring Framework предлагает простую абстракцию для отправки электронной почты, а Spring Boot производит автоматическую настройку этого интерфейса и стартового модуля.
1. Зависимости Maven
Чтобы добавить функцию автоконфигурации Spring Boot в приложение, нам нужно внести spring-boot-starter-mail в файл pom.xml. Ниже представлен фрагмент нашего файла pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2. Конфигурация электронной почты
После того как мы определили зависимости, следующим шагом будет добавление конфигурации почты для автоматической настройки с помощью Spring Boot:
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username= gmail user name
spring.mail.password= your password
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
Приведенные выше значения конфигурации относятся к SMTP-серверу Gmail. При необходимости замените их своими
3. Служба электронной почты
Далее мы создадим простой класс службы электронной почты, который позволит нашему приложению отправлять электронные письма. В нашем примере почтовой службы мы реализуем два метода:
отправка простых электронных писем;
отправка электронных писем с вложением.
Наш код будет выглядеть так:
@Service
public class DefaultEmailService implements EmailService {
@Autowired
public JavaMailSender emailSender;
@Override
public void sendSimpleEmail(String toAddress, String subject, String message) {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setTo(toAddress);
simpleMailMessage.setSubject(subject);
simpleMailMessage.setText(message);
emailSender.send(simpleMailMessage);
}
@Override
public void sendEmailWithAttachment(String toAddress, String subject, String message, String attachment) throws MessagingException, FileNotFoundException {
MimeMessage mimeMessage = emailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true);
messageHelper.setTo(toAddress);
messageHelper.setSubject(subject);
messageHelper.setText(message);
FileSystemResource file = new FileSystemResource(ResourceUtils.getFile(attachment));
messageHelper.addAttachment("Purchase Order", file);
emailSender.send(mimeMessage);
}
}
4. Тестовый контроллер
Создадим простой контроллер REST, чтобы проверить, как работает код. У нашего контроллера REST будет два метода. Сам контроллер будет выглядеть так:
package com.javadevjournal.controller;
import com.javadevjournal.email.service.EmailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.MailException;
import org.springframework.web.bind.annotation.*;
import javax.mail.MessagingException;
import java.io.FileNotFoundException;
@RestController
@RequestMapping("/email")
public class EmailController {
private static final Logger LOG = LoggerFactory.getLogger(EmailController.class);
@Autowired
EmailService emailService;
@GetMapping(value = "/simple-email/{user-email}")
public @ResponseBody ResponseEntity sendSimpleEmail(@PathVariable("user-email") String email) {
try {
emailService.sendSimpleEmail(email, "Welcome", "This is a welcome email for your!!");
} catch (MailException mailException) {
LOG.error("Error while sending out email..{}", mailException.getStackTrace());
return new ResponseEntity<>("Unable to send email", HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>("Please check your inbox", HttpStatus.OK);
}
@GetMapping(value = "/simple-order-email/{user-email}")
public @ResponseBody ResponseEntity sendEmailAttachment(@PathVariable("user-email") String email) {
try {
emailService.sendEmailWithAttachment(email, "Order Confirmation", "Thanks for your recent order",
"classpath:purchase_order.pdf");
} catch (MessagingException | FileNotFoundException mailException) {
LOG.error("Error while sending out email..{}", mailException.getStackTrace());
return new ResponseEntity<>("Unable to send email", HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>("Please check your inbox for order confirmation", HttpStatus.OK);
}
}
5. Основной класс Spring Boot
@SpringBootApplication
public class SendEmailUsingSpringApplication {
public static void main(String[] args) {
SpringApplication.run(SendEmailUsingSpringApplication.class, args);
}
}
Запустите приложение и перейдите по следующим ссылкам:
http://localhost:8080/email/order-email/umeshawasthi@www.javadevjournal.com
http://localhost:8080/email/simple-email/umeshawasthi@www.javadevjournal.com
6. Конфигурация SMTP-сервера Gmail
Прежде чем использовать SMTP-сервер Gmail, ознакомьтесь со следующей статьей. Из-за дополнительных мер безопасности в Gmail ваш обычный пароль от электронной почты работать не будет.
Отправка электронных писем с помощью Spring Boot и Thymeleaf
В предыдущем разделе мы рассмотрели, как отправлять электронные письма с помощью Spring. Однако для рабочего приложения понадобится более продвинутый способ. В этом разделе мы рассмотрим, как отправлять электронные письма с помощью Spring Boot и Thymeleaf.
Thyleleaf — это шаблонизатор. Thymeleaf поможет нам отправлять электронные письма в формате HTML с широкими функциональными возможностями. Предположим, мы хотим отправить клиенту электронное письмо для подтверждения его учетной записи.
7. Класс EmailContext
Для большей универсальности создадим класс EmailContext
, содержащий всю основную информацию, которая нам нужна для электронной почты. Наш класс будет выглядеть так:
public abstract class AbstractEmailContext {
private String from;
private String to;
private String subject;
private String email;
private String attachment;
private String fromDisplayName;
private String emailLanguage;
private String displayName;
private String templateLocation;
private Map <String, Object> context;
//getter and seter methods
}
Рассмотрим несколько полей этого класса:
templateLocation
— указывает расположение HTML-шаблона. Это поле понадобится нам для генерации окончательного вывода.context
— содержит все динамические значения, передаваемые в электронном письме. Thymeleaf будет использовать эту карту для замены выражений фактическими значениями (например, ваше имя и т. д.).
Далее мы настроим нашу службу электронной почты на обработку HTML-шаблонов Thymeleaf для наполнения электронных писем.
8. Реализация EmailService
package com.javadevjournal.core.email.service;
import com.javadevjournal.core.email.context.AbstractEmailContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.nio.charset.StandardCharsets;
@Service
public class DefaultEmailService implements EmailService {
@Autowired
private JavaMailSender emailSender;
@Autowired
private SpringTemplateEngine templateEngine;
@Override
public void sendMail(AbstractEmailContext email) throws MessagingException {
MimeMessage message = emailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message,
MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
StandardCharsets.UTF_8.name());
Context context = new Context();
context.setVariables(email.getContext());
String emailContent = templateEngine.process(email.getTemplateLocation(), context);
mimeMessageHelper.setTo(email.getTo());
mimeMessageHelper.setSubject(email.getSubject());
mimeMessageHelper.setFrom(email.getFrom());
mimeMessageHelper.setText(emailContent, true);
emailSender.send(message);
}
}
В приведенном выше коде есть несколько важных моментов.
Мы создаем и настраиваем карту как набор переменных.
Thymeleaf
будет использовать этот контекст для замены заполнителей или выражений значениями, переданными через него.SpringTemplateEngine
представляет собой абстракцию различных шаблонизаторов, эта функция осуществляет выбор настроенного шаблонизатора. В нашем случае этоThymeleaf
.Функция
templateEngine.process()
отвечает за обработку и возврат результата в виде строки. Она выбирает шаблон HTML, обработает его и заменяет выражения фактическими значениями.Наконец, мы встраиваем получившийся результат в
MimeMessage
и заставляем почтовый сервер обрабатывать его как HTML (mimeMessageHelper.setText(emailContent, true)
)
9. Шаблон электронного письма
Вот наш HTML-шаблон для электронного письма (для экономии места я немного сократил HTML-код).
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="utf-8"> <!-- utf-8 works for most cases -->
<meta name="viewport" content="width=device-width"> <!-- Forcing initial-scale shouldn't be necessary -->
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- Use the latest (edge) version of IE rendering engine -->
<meta name="x-apple-disable-message-reformatting"> <!-- Disable auto-scale in iOS 10 Mail entirely -->
<title></title> <!-- The title tag shows in email notifications, like Android 4.4. -->
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700" rel="stylesheet">
<!-- CSS Reset : BEGIN -->
</head>
<body width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #f1f1f1;">
<center style="width: 100%; background-color: #f1f1f1;">
<div style="display: none; font-size: 1px;max-height: 0px; max-width: 0px; opacity: 0; overflow: hidden; mso-hide: all; font-family: sans-serif;">
‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌
</div>
<div style="max-width: 600px; margin: 0 auto;" class="email-container">
<!-- BEGIN BODY -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: auto;">
<tr>
<td valign="top" class="bg_white" style="padding: 1em 2.5em 0 2.5em;">
<table role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td class="logo" style="text-align: center;">
<h1>Welcome</h1>
</td>
</tr>
</table>
</td>
</tr><!-- end tr -->
<tr>
<td valign="middle" class="hero bg_white" style="padding: 2em 0 4em 0;">
<table>
<tr>
<td>
<div class="text" style="padding: 0 2.5em; text-align: center;">
<h2>Finish creating your account.</h2>
<h3>Hi<p th:text="${firstName}"></h3>
<h3>We're excited to have you get started. First, you need to confirm your account. Just press the button below.</h3>
<p><a th:href="${verificationURL}" class="btn btn-primary">Validate Account</a></p>
</div>
</td>
</tr>
<tr>
<td>
<div class="text" style="padding: 0 2.5em; text-align: center;">
<h3>if you experience any issues with the button above, copy and paste the URL below into your web browser.</h3>
<p th:text="${verificationURL}"></p>
</div>
</td>
</tr>
</table>
</td>
</tr><!-- end tr -->
<!-- 1 Column Text + Button : END -->
</table>
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: auto;">
<tr>
<td valign="middle" class="bg_light footer email-section">
<table>
<tr>
<td valign="top" width="33.333%" style="padding-top: 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="text-align: left; padding-right: 10px;">
<h3 class="heading">About</h3>
<p>Welcome to Java Development Journal Blog. We publish articles on
Spring, Spring Boot and Spring Security.
</p>
</td>
</tr>
</table>
</td>
<td valign="top" width="33.333%" style="padding-top: 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="text-align: left; padding-left: 5px; padding-right: 5px;">
<h3 class="heading">Contact Info</h3>
<ul>
<li><span class="text">Java Development Journal</span></li>
</ul>
</td>
</tr>
</table>
</td>
<td valign="top" width="33.333%" style="padding-top: 20px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="text-align: left; padding-left: 10px;">
<h3 class="heading">Useful Links</h3>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
</ul>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr><!-- end: tr -->
</table>
</div>
</center>
</body>
</html>
Имейте в виду, что необходимо внести в контекст значения для ${firstName}
и ${verifyURL}
, чтобы Thymeleaf
мог подставить фактические значения для этих переменных во время обработки.
Резюме
В этой статье мы узнали, как отправлять электронные письма с помощью Spring. Мы узнали, каким образом можно реализовать функции электронной почты с применением автоконфигурации Spring Boot. Исходный код, приведенный в этом посте, доступен на GitHub.
Перевод материала подготовили в преддверии старта курса онлайн-курса «Разработчик на Spring Framework».
Приглашаем также всех желающих на открытый вебинар «Spring в Docker. Практические советы». На занятии мы рассмотрим всем известную тему по запуску Spring-приложений в Docker-контейнере и не всем известные решения возникающих задач.