Spring Boot 发送邮件
简单邮件传输协议 (SMTP) 是一种以电子方式传输邮件的标准通信协议。 SMTP 使从应用程序内部发送邮件消息成为可能。 在本教程中,我们将使用 SMTP 和 Spring Boot 从我们的应用程序发送邮件消息。
1. Maven 依赖
<!--mail-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
如果需要指定版本,也可以从mvn repo中找到你需要的版本号。
2. Spring Mail 服务简介
Spring mail 是Spring 框架提供的一个程序库,用于发送电子邮件,使我们不受底层邮件系统的限制,只关注客户端进行资源处理。Spring mail 包的内容如下:
MailSender
接口:核心接口,提供用于发送简单电子邮件的基本功能。JavaMailSender
接口:上述MailSender的子接口。它支持MIME消息,并且通常与MimeMessageHelper类结合使用以创建MimeMessage。JavaMailSenderImpl
类:提供JavaMailSender接口的实现。它支持MimeMessage和SimpleMailMessage。SimpleMailMessage
类:用于创建简单的邮件,包括from
(发送者),to
(接收者),cc
(抄送),subject
(主题)和text
(文本)等字段。MimeMessagePreparator
接口:提供用于接收MIME消息的回调接口。MimeMessageHelper
类:用于创建MIME消息的帮助器类。它提供在HTML布局中对图像,典型邮件附件和文本内容的支持。
3. 邮件服务配置
引入maven依赖之后,下一步就是使用spring.mail.*
namespace 在 application.properties
文件中配置邮件服务。
spring:
mail:
#字符集编码 默认 UTF-8
default-encoding: UTF-8
#邮件服务器的地址:例如smtp.qq.com,smtp.gmail.com,端口:25/465/587
host: localhost
#登陆服务器的用户名和密码
username: username
password: password
port: 25
properties:
mail:
debug: false
smtp:
debug: false
auth: true
#启用tls连接
starttls: true
# smtp 服务器需要身份验证,所以要配置用户密码
protocol: smtp
test-connection: false
当我们的邮件服务是SSL安全连接的时候,我们需要添加ssl相关的信息:
spring:
mail:
host: localhost
port: 465
username: username
password: password
properties:
mail:
smtp:
auth: true
ssl:
enable: true
trust: smtp.gmail.com
trust: smtp.gmail.com
这个配置可以让我们避免一些关于PKIX路径错误的报错,不需要添加证书到java的security文件中,否则会报下面的这种错误:
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
... 29 more
当然不同的邮件服务器的配置有一些细微的差异,具体的可以参考Spring Boot 发送邮件全解析这篇文章。
4. 发送邮件
4.1 发送简单邮件
@Service
public class MailServiceImpl implements MailService {
@Autowired
private JavaMailSender javaMailSender;
@Override
public void sendSimpleMessage() {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("abc@qq.com");
message.setTo("efd@qq.com");
message.setSubject("Test send simple mail message");
message.setText("Hello world!");
javaMailSender.send(message);
}
}
4.2 发送附件邮件
@Override
public void sendMessageWithAttachment() {
MimeMessage message = javaMailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("to@qq.com");
helper.setFrom("from@qq.com");
helper.setSubject("Send attachment file to email");
helper.setText("attachment file...");
FileSystemResource file = new FileSystemResource(new File("Absolute path"));
helper.addAttachment("Invoice", file);
javaMailSender.send(message);
} catch (MessagingException ex) {
logger.error("Failed to send email to. error={}", ex.getMessage());
}
}
4.3 发送模板邮件
Spring 中可以作为邮件模板的有几个选择:Velocity,Freemarker,Thymeleaf。 SpringBoot 1.4.0以后 Velocity 废弃了,官方建议用Freemarker。而Thymeleaf的效率没有freemaker高(评测见参考文章【4】)。
Freemarker 的语法可以参考官网的手册:https://freemarker.apache.org/docs/index.html 中文手册:https://sourceforge.net/projects/freemarker/files/chinese-manual/
同样,我们先引入 Freemarker 的 Maven 依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
然后在项目的 /resoource/templates
目录下添加一个 Freemarker 模板文件 notification.flt
。
<html>
<head>
<title>Hello world!</title>
</head>
<body>
<h1>Hello</h1>
<p>My name is ${name}</p>
</body>
</html>
在 SpringBoot 的配置文件中加上 Freemarker 的相关配置:
spring:
freemarker:
template-loader-path: classpath:/templates/
enabled: true
cache: false
charset: UTF-8
content-type: text/html
check-template-location: true
在 EmailServiceImpl
中使用 Freemarker 模板发送邮件:
@Service
public class MailServiceImpl implements MailService {
private static final Logger logger = LoggerFactory.getLogger(MailServiceImpl.class);
@Autowired
private JavaMailSender javaMailSender;
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@Override
public void sendMessageWithFreemarkerTemplate() {
MimeMessage message = javaMailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("to@qq.com");
helper.setFrom("from@qq.com");
helper.setSubject("Send freemarker template to email");
HashMap<String, Object> models = new HashMap<>();
models.put("name", "freemarker");
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("notification.flt");
String text = FreeMarkerTemplateUtils.processTemplateIntoString(template, models);
helper.setText(text);
javaMailSender.send(message);
} catch (Exception ex) {
logger.error("Failed to send email to. error={}", ex.getMessage());
}
}
}
至此实现了三中方式:纯文本,富文本(图片/附件),Freemarker模版的邮件发送功能,接下来就来测试一下我们的邮件是否能发送出去吧。
5. 测试邮件发送服务
我们为了测试邮件服务的发送功能,暂时可以先不用使用真正的邮件服务器,而是换成GreenMail。 GreenMail是用于测试目的的电子邮件服务器,可以用于邮件集成测试或用于开发的轻量级沙盒邮件服务器。具体的使用方法可以参考官网上的用例。
这里先引入GreenMail 的Maven依赖:
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<version>1.5.11</version>
<scope>test</scope>
</dependency>
在Spring boot 的测试文件(application-test.yaml)中配置用于邮件服务的相关属性。
spring:
mail:
default-encoding: UTF-8
host: localhost
username: abc@qq.com
password: password
port: 3025
properties:
mail:
debug: false
smtp:
debug: false
auth: true
starttls: true
protocol: smtp
test-connection: false
我们创建一个自定义的 JUnit Rule 来初始化和停止GreenMail邮件服务器。
public class SmtpServerRule extends ExternalResource {
# 设置发送邮件服务的用户的用户名
private static final String USER_PASSWORD = "password";
# 设置发送邮件服务用户的密码
private static final String USER_NAME = "abc@qq.com";
private GreenMail smtpServer;
@Override
protected void before() throws Throwable {
super.before();
smtpServer = new GreenMail(ServerSetupTest.SMTP);
smtpServer.start();
// setup user on the mail server
smtpServer.setUser(USER_NAME, USER_PASSWORD);
}
public MimeMessage[] getMessage() {
return smtpServer.getReceivedMessages();
}
@Override
protected void after() {
super.after();
smtpServer.stop();
}
}
然后就可以开始写我们的测试用例了。 我们使用JUnit @Rule注解配置SmtpServerRule。这标记了在每个集成测试之前和之后要调用的自定义规则。并允许我们拦截传入的电子邮件。最后,我们做出一些断言并验证发送的电子邮件是否等于接收的电子邮件。
@ActiveProfiles("test")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
public class MailServiceImplTest {
@Rule
public SmtpServerRule smtpServerRule = new SmtpServerRule();
@Autowired
private MailService mailService;
@Test
public void sendSimpleMessage() throws MessagingException {
mailService.sendSimpleMessage();
MimeMessage[] messages = smtpServerRule.getMessage();
assertEquals("Test send simple mail message", messages[0].getSubject());
assertEquals("abc@qq.com", messages[0].getFrom()[0].toString());
assertEquals("efd@qq.com", messages[0].getAllRecipients()[0].toString());
}
}
另外的两种邮件参考上面的代码实现。
6. 总结
在这篇文章中,我们展示了如何通过Spring Boot应用程序设置和发送电子邮件。所有这些示例和代码片段的实现都可以在MyGitHub项目中找到。
7. 参考文章
【1】Testing mail code in Spring Boot application
【2】Spring Mail Integration Testing with JUnit and GreenMail Example