发送邮件是一个非常常见的需求,用户注册、找回密码等都会用到。使用 JavaSE 代码发送邮件的步骤非常繁琐,Spring Boot 因为提供了相关的自动化配置类,使得相关工作容易了很多。本章我们讨论使用 Spring Boot 发送邮件的几种不同方法。

22.1 邮件基础

假设从 user1@qq.com 发送邮件到 user2@163.com :

  1. user1@qq.com 先将邮件投递到腾讯的邮件服务器
  2. 腾讯的邮件服务器将我们的邮件投递到网易的邮件服务器
  3. 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 邮箱网页版,点击上方的设置按钮:
image.png
然后点击账户选项卡:
image.png
在账户选项卡中找到开启POP3/SMTP选项,如下:
image.png
点击开启,开启相关功能,开启过程需要手机号码验证,按照步骤操作即可,不赘述。开启成功之后,即可获取一个授权码,将该号码保存好,一会使用。

22.3 邮件的依赖

我们在最开始创建项目的时候已经在在I/O类别下选择了Java Mail Sender
截屏2021-05-31 上午10.39.25.png
如果没有在创建的时候选择,也可以自己手工添加

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-mail</artifactId>
  4. </dependency>

22.4 配置邮箱基本信息

现在在 application.yml 中配置邮箱的基本信息:

  1. spring:
  2. mail:
  3. host: smtp.qq.com
  4. port: 587
  5. username: 1497305250@qq.com
  6. password: ****************
  7. default-encoding: UTF-8
  8. properties.mail.smtp.socketFactoryClass: javax.net.ssl.SSLSocketFactory
  9. 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;

版权说明:本文由北京朗思云网科技股份有限公司原创,向互联网开放全部内容但保留所有权力。