发送邮件是一个非常常见的需求,用户注册、找回密码等都会用到。使用 JavaSE 代码发送邮件的步骤非常繁琐,Spring Boot 因为提供了相关的自动化配置类,使得相关工作容易了很多。本章我们讨论使用 Spring Boot 发送邮件的几种不同方法。
22.1 邮件基础
假设从 user1@qq.com 发送邮件到 user2@163.com :
- user1@qq.com 先将邮件投递到腾讯的邮件服务器
- 腾讯的邮件服务器将我们的邮件投递到网易的邮件服务器
- user2@163.com 登录网易的邮件服务器查看邮件
邮件投递大致就是这个过程,这个过程就涉及到了多个协议,我们来分别看一下。
1. SMTP 协议
SMTP 协议全称为 Simple Mail Transfer Protocol,译作简单邮件传输协议,它定义了邮件客户端软件与 SMTP 服务器之间,以及 SMTP 服务器与 SMTP 服务器之间的通信规则。
SMTP 是一个基于 TCP/IP 的应用层协议,服务器默认监听的端口号为 25 ,江湖地位有点类似于 HTTP。 它是基于 TCP/IP 的应用层协议,所以可通过 Socket 发送邮件。
user1@qq.com 用户先将邮件投递到腾讯的 SMTP 服务器这个过程就使用了 SMTP 协议,然后腾讯的 SMTP 服务器将邮件投递到网易的 SMTP 服务器这个过程也依然使用了 SMTP 协议,SMTP 服务器就是用来接收并替用户发送邮件的服务器。
2. POP3 协议
POP3 协议全称为 Post Office Protocol ,译作邮局协议,它定义了邮件客户端与 POP3 服务器之间的通信规。当邮件到达网易的 SMTP 服务器之后, user2@163.com 用户需要登录服务器查看邮件,这时用的就是 POP3 协议。邮件服务商会为每一个用户提供专门的邮件存储空间,SMTP 服务器收到邮件之后,就将邮件保存到相应用户的邮件存储空间中,如果用户要读取邮件,就需要通过邮件服务商的 POP3 邮件服务器来完成。
3. IMAP
这个协议是对 POP3 协议的扩展,作用类似但功能更强,它支持邮件客户端和存储邮件的 IMAP 服务器进行内容的同步。
22.2 准备工作
目前国内大部分的邮件服务商都不允许直接使用用户名/密码的方式来在代码中发送邮件,都是要先申请授权码,这里以 QQ 邮箱为例,向大家演示授权码的申请流程:首先我们需要先登录 QQ 邮箱网页版,点击上方的设置按钮:
然后点击账户选项卡:
在账户选项卡中找到开启POP3/SMTP选项,如下:
点击开启,开启相关功能,开启过程需要手机号码验证,按照步骤操作即可,不赘述。开启成功之后,即可获取一个授权码,将该号码保存好,一会使用。
22.3 邮件的依赖
我们在最开始创建项目的时候已经在在I/O类别下选择了Java Mail Sender
如果没有在创建的时候选择,也可以自己手工添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
22.4 配置邮箱基本信息
现在在 application.yml 中配置邮箱的基本信息:
spring:
mail:
host: smtp.qq.com
port: 587
username: 1497305250@qq.com
password: ****************
default-encoding: UTF-8
properties.mail.smtp.socketFactoryClass: javax.net.ssl.SSLSocketFactory
properties.mail.debug: true
配置含义分别如下:
- 配置 SMTP 服务器地址
- SMTP 服务器的端口
- 配置邮箱用户名
- 配置密码,注意,不是真正的密码,而是刚刚申请到的授权码
- 默认的邮件编码
- 配饰 SSL 加密工厂
- 表示开启 DEBUG 模式,这样,邮件发送过程的日志会在控制台打印出来,方便排查错误
如果不知道 smtp 服务器的端口或者地址的的话,可以参考 腾讯的邮箱文档
做完这些之后,Spring Boot 就会自动帮我们配置好邮件发送类,接下来就可以编写发送邮件的代码。
22.5 实现发送邮件的基本方法
22.5.1 发送简单邮件
简单邮件就是指邮件内容是一个普通的文本文档:
package com.longser.union.cloud.extended;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import java.util.Date;
@SpringBootTest
public class SendMailTest {
@Autowired
JavaMailSender javaMailSender;
@Test
public void sendSimpleMail() {
// 1. 构建一个邮件对象
SimpleMailMessage message = new SimpleMailMessage();
// 2. 设置邮件主题
message.setSubject("这是一封测试邮件");
// 3. 设置邮件发送者
message.setFrom("1497305250@qq.com");
// 4. 设置邮件接收者,可以有多个接收者
message.setTo("davidjia72@sina.com");
// 5. 设置邮件抄送人,可以有多个抄送人
message.setCc("david@longser.com.cn");
// 6. 设置隐秘抄送人,可以有多个
message.setBcc("david@longser.com.cn");
// 7. 设置邮件发送日期
message.setSentDate(new Date());
// 8. 设置邮件的正文
message.setText("这是测试邮件的正文");
// 9. 发送邮件
javaMailSender.send(message);
}
}
22.5.2 发送带附件的邮件
邮件的附件可以是图片也可以是普通文件,作为附件来处理的时候和文件格式没有直接的关系。
@Test
public void sendAttachFileMail() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
helper.setSubject("这是一封带图片附件的测试邮件");
helper.setFrom("1497305250@qq.com");
helper.setTo("davidjia72@sina.com");
helper.setCc("david@longser.com.cn");
helper.setBcc("david@longser.com.cn");
helper.setSentDate(new Date());
helper.setText("这是测试邮件的正文");
helper.addAttachment("test.jpg",
new File("src/main/resources/static/007.jpg"));
javaMailSender.send(mimeMessage);
}
注意这里在构建邮件对象时和前文有所差异——这里通过 javaMailSender 来获取一个复杂邮件对象,然后再利用 MimeMessageHelper 对邮件进行配置,MimeMessageHelper 是一个邮件配置的辅助工具类,创建时候的 true 表示构建一个 multipart message
类型的邮件,有了 MimeMessageHelper 之后,我们针对邮件的配置都是通过 MimeMessageHelper 来完成。最后通过 addAttachment 方法来添加一个附件。
22.5.3 发送带图片资源的邮件
图片资源和附件中的图片不同图片资源是放在邮件正文中的,打开邮件就能看到图片(一般来说不建议使用这种方式,接收方可能会对邮件正文内容的大小有限制)。
@Test
public void sendImgResMail() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("这是一封内嵌图片的测试邮件");
helper.setFrom("1497305250@qq.com");
helper.setTo("davidjia72@sina.com");
helper.setCc("david@longser.com.cn");
helper.setBcc("david@longser.com.cn");
helper.setSentDate(new Date());
helper.setText("<p>hello 大家好,这是一封测试邮件,这封邮件包含一张图片</p><p>这张图片是:</p><img src='cid:p01'/>",true);
helper.addInline("p01",new FileSystemResource(new File("src/main/resources/static/007.jpg")));
javaMailSender.send(mimeMessage);
}
这里的邮件 text 是一个 HTML 文本,里边涉及到的图片资源先用一个占位符占着,setText 方法的第二个参数 true 表示第一个参数是一个 HTML 文本。setText 之后,再通过 addInline 方法来添加图片资源。
22.6 邮件内容使用模板文件
22.6.1 使用 Freemarker 作邮件模板
在实际开发中,很少使用第 1 种和第 3 种方法。因为邮件内容会通常比较丰富,所以都是通过 HTML 来呈现。如果直接拼接 HTML 字符串,不仅繁琐也不利于后期维护,所以为了解决这个问题通常会使用邮件模板。最具代表性的两个模板就是 Freemarker 模板和 Thyemeleaf 模板。
首先需要引入 Freemarker 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
然后在 resources/templates 目录下创建一个 mail.ftl 作为邮件发送模板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>hello 欢迎注册 工建云 软件,您的注册信息如下:</p>
<table border="1">
<tr>
<td>用户名</td>
<td>${userName}</td>
</tr>
<tr>
<td>手机号码</td>
<td>${mobile}</td>
</tr>
</table>
<div style="color: #ff1a0e">北京朗思云网科技股份有限公司</div>
</body>
</html>
接下来,将邮件模板渲染成 HTML ,然后发送即可。
@Test
public void sendFreemarkerMail() throws MessagingException, IOException, TemplateException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("这是一封测试邮件");
helper.setFrom("1497305250@qq.com");
helper.setTo("davidjia72@sina.com");
helper.setCc("david@longser.com.cn");
helper.setBcc("david@longser.com.cn");
helper.setSentDate(new Date());
//构建 Freemarker 的基本配置
Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);
// 配置模板位置
ClassLoader loader = CloudApplication.class.getClassLoader();
configuration.setClassLoaderForTemplateLoading(loader, "templates");
//加载模板
Template template = configuration.getTemplate("mail.ftl");
UserEntry user = new UserEntry();
user.setUserName("david");
user.setMobile("13808881234");
StringWriter out = new StringWriter();
//模板渲染,渲染的结果将被保存到 out 中 ,将out 中的 html 字符串发送即可
template.process(user, out);
helper.setText(out.toString(),true);
javaMailSender.send(mimeMessage);
}
上面代码中的 Configuration 需要 import freemarker.template.Configuration;
需要注意的是,虽然引入了 Freemarker 的自动化配置,但是我们在这里是直接 new Configuration 来重新配置 Freemarker 的,所以 Freemarker 默认的配置这里不生效,因此,在填写模板位置时,值为 templates 。
22.6.52 使用 Thymeleaf 作邮件模板
在 Spring Boot 中来构建邮件模板的时候,推荐使用 Thymeleaf。因为它的自动化配置提供了一个 Template Engine,可以方便的将 Thymeleaf 模板渲染为 HTML ,同时,Thymeleaf 的自动化配置在这里是继续有效的 。
首先,引入 Thymeleaf 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
然后,创建 Thymeleaf 邮件模板:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>hello 欢迎注册 工建云 软件,您的注册信息如下:</p>
<table border="1">
<tr>
<td>用户名</td>
<td>${userName}</td>
</tr>
<tr>
<td>手机号码</td>
<td>${mobile}</td>
</tr>
</table>
<div style="color: #ff1a0e">北京朗思云网科技股份有限公司</div>
</body>
</html>
接下来发送邮件:
@Test
public void sendThymeleafMail() throws MessagingException {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
helper.setSubject("这是一封测试邮件");
helper.setFrom("1497305250@qq.com");
helper.setTo("davidjia72@sina.com");
helper.setCc("david@longser.com.cn");
helper.setBcc("david@longser.com.cn");
helper.setSentDate(new Date());
Context context = new Context();
context.setVariable("userName", "david");
context.setVariable("mobile","13808881234");
String process = templateEngine.process("mail.html", context);
helper.setText(process,true);
javaMailSender.send(mimeMessage);
}
上面代码中的 Context 需要 import org.thymeleaf.context.Context;
版权说明:本文由北京朗思云网科技股份有限公司原创,向互联网开放全部内容但保留所有权力。