SpringBoot 邮件发送

54 阅读5分钟

Spring Boot 邮件发送核心模块,支持 普通文本邮件、HTML 富文本邮件、带附件邮件、Thymeleaf 模板邮件 四大高频场景,代码可直接复制复用,适配日常开发中 90%+ 的邮件发送需求。

一、环境配置

1.1 获取邮箱授权码

QQ 邮箱获取方式:

  1. 登录 QQ 邮箱 → 进入「设置」→「账户」;
  2. 下滑找到「POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务」;
  3. 开启「POP3/SMTP 服务」,按提示发送短信验证;
  4. 验证成功后,系统生成的「授权码」即为 spring.mail.password 的值。

163 邮箱获取方式:

  1. 登录 163 邮箱 → 进入「设置」→「POP3/SMTP/IMAP」;
  2. 开启「SMTP 服务」,设置「授权码」(需验证手机);
  3. 生成的授权码用于配置 spring.mail.password。

1.2 引入spring-boot-starter-mail依赖。

<!--邮件通知-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>3.5.0</version>
</dependency>

<!-- Thymeleaf(用于模板邮件,可选) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

... 其他必要依赖

1.3 application.yaml 邮箱信息配置

spring:
  # 邮件配置(其他邮箱如 163、企业邮箱配置类似,仅需修改 host 和授权码)
  mail:
    host: smtp.qq.com  # 邮件服务器地址(QQ邮箱:smtp.qq.com;163邮箱:smtp.163.com)
    port: 587          # 端口(SSL端口465,非SSL端口587,推荐587)
    username: 1979774006@qq.com  #配置邮箱用户名;你自己的邮箱
    password: hpsumfzfzgfgeaig  #配置申请到的授权码;这里填写刚才短信申请到的授权码
    default-encoding: UTF-8   #配置邮件编码
    protocol: smtp   #协议
    properties:
      mail:
        smtp:
          auth: true   #开启认证
          starttls:
            enable: true # 开启STARTTLS加密(适配587端口)
            required: true

  # Thymeleaf配置(模板邮件用)
  thymeleaf:
    prefix: classpath:/templates/  # 模板文件存放路径
    suffix: .html
    mode: HTML
    encoding: UTF-8
    cache: false  # 开发环境关闭缓存,实时生效

# 自定义邮件配置(可选,统一管理收件人、主题前缀等)
email:
  default-to: g13345092105@qq.com  # 默认收件人
  subject-prefix: 【Java技术Demo】  # 邮件主题前缀

按顺序完成如上3个步骤,发送邮件的基础环境配置就弄完了。

二、代码实现

2.1 参数类

定义邮件发送的实体参数

@Data
@ApiModel(value = "邮件发送参数",description = "邮件发送参数DTO")
public class EmailDTO {

    @ApiModelProperty("收件人(多个用逗号分隔,如 "a@xxx.com,b@xxx.com")")
    private String to;

    @ApiModelProperty("邮件主题")
    private String subject;

    @ApiModelProperty("邮件内容(文本/HTML)")
    private String content;

    @ApiModelProperty("是否为HTML邮件(true=HTML,false=文本)")
    private boolean isHtml;

    @ApiModelProperty("附件路径列表(本地文件路径)")
    private List<String> attachments;

    @ApiModelProperty("模板邮件时,模板名称")
    private String templateName;

    @ApiModelProperty("模板邮件时,模板参数")
    private Object templateData;

}

2.2 核心服务类

通过调用邮件服务实现发送邮件功能

/**
 * 邮件发送核心服务:支持文本邮件、HTML邮件、带附件邮件、Thymeleaf模板邮件
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class EmailService {

    // 注入Spring邮件发送核心bean(自动配置,无需手动创建)
    @Resource
    private final JavaMailSender javaMailSender;

    // Thymeleaf模板引擎(模板邮件用)
    @Resource
    private final TemplateEngine templateEngine;

    // 发送者邮箱(从配置文件读取)
    @Value("${spring.mail.username}")
    private String from;

    // 默认收件人(从配置文件读取)
    @Value("${email.default-to}")
    private String defaultTo;

    // 邮件主题前缀(从配置文件读取)
    @Value("${email.subject-prefix}")
    private String subjectPrefix;

    /**
     * 通用邮件发送方法(统一入口)
     */
    public Result<String> sendEmail(EmailDTO emailDTO) {
        try {
            // 1. 构建MIME消息(支持HTML和附件)
            MimeMessage mimeMessage = javaMailSender.createMimeMessage();
            // true:支持多部分(附件、HTML)
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");

            // 2. 基础配置(发件人、收件人、主题)
            helper.setFrom(from); // 发件人(必须与配置文件username一致)
            helper.setTo(Objects.nonNull(emailDTO.getTo()) ? emailDTO.getTo().split(",") : defaultTo.split(",")); // 收件人(支持多个)
            helper.setSubject(subjectPrefix + emailDTO.getSubject()); // 主题(拼接前缀)

            // 3. 处理邮件内容(文本/HTML/模板)
            if (Objects.nonNull(emailDTO.getTemplateName())) {
                // 场景1:模板邮件(Thymeleaf)
                Context context = new Context();
                context.setVariables((java.util.Map<String, Object>) emailDTO.getTemplateData());
                // 渲染模板为HTML内容
                String htmlContent = templateEngine.process(emailDTO.getTemplateName(), context);
                helper.setText(htmlContent, true); // true:标记为HTML
            } else {
                // 场景2:文本/HTML邮件
                helper.setText(emailDTO.getContent(), emailDTO.isHtml());
            }

            // 4. 处理附件(如有)
            if (Objects.nonNull(emailDTO.getAttachments()) && !emailDTO.getAttachments().isEmpty()) {
                for (String attachmentPath : emailDTO.getAttachments()) {
                    File file = new File(attachmentPath);
                    if (file.exists()) {
                        FileSystemResource resource = new FileSystemResource(file);
                        // 添加附件(参数:附件名称,资源)
                        helper.addAttachment(file.getName(), resource);
                    } else {
                        return Result.fail( "附件不存在:" + attachmentPath);
                    }
                }
            }

            // 5. 发送邮件
            javaMailSender.send(mimeMessage);
            log.info("邮件发送成功!收件人:{}", Objects.nonNull(emailDTO.getTo()) ? emailDTO.getTo() : defaultTo);
            return Result.ok("邮件发送成功!收件人:" + (Objects.nonNull(emailDTO.getTo()) ? emailDTO.getTo() : defaultTo));

        } catch (MessagingException e) {
            log.error("邮件发送失败:{}", e.getMessage());
            return Result.fail("邮件发送失败:" + e.getMessage());
        }
    }

    // -------------------------- 以下为简化方法(快速调用)--------------------------
    /**
     * 发送普通文本邮件(简化版)
     * 该方法提供了一个简化的接口用于发送纯文本邮件,无需构建复杂的EmailDTO对象
     *
     * @param to 收件人邮箱地址
     * @param subject 邮件主题
     * @param content 邮件内容(纯文本)
     * @return 返回操作结果,包含发送状态信息
     */
    public Result<String> sendTextEmail(String to, String subject, String content) {
        // 创建EmailDTO对象并设置邮件基本信息
        EmailDTO dto = new EmailDTO();
        dto.setTo(to);                    // 设置收件人地址
        dto.setSubject(subject);          // 设置邮件主题
        dto.setContent(content);          // 设置邮件内容
        dto.setHtml(false);               // 标记为非HTML格式,即纯文本邮件
        return sendEmail(dto);            // 调用实际发送邮件的方法并返回结果
    }

    /**
     * 发送HTML邮件(简化版)
     * 该方法提供了一个简化的接口用于发送HTML格式的邮件
     * @param to 收件人邮箱地址
     * @param subject 邮件主题
     * @param htmlContent HTML格式的邮件内容
     * @return 返回操作结果,包含发送状态和信息
     */
    public Result<String> sendHtmlEmail(String to, String subject, String htmlContent) {
        // 创建EmailDTO对象,用于封装邮件信息
        EmailDTO dto = new EmailDTO();
        dto.setTo(to);                  // 设置收件人地址
        dto.setSubject(subject);        // 设置邮件主题
        dto.setContent(htmlContent);    // 设置邮件内容(HTML格式)
        dto.setHtml(true);              // 标记该邮件为HTML格式
        return sendEmail(dto);          // 调用sendEmail方法发送邮件并返回结果
    }

    /**
     * 发送带附件的邮件(简化版)
     * 该方法提供了一个简化的接口用于发送带附件的邮件,内部通过构造EmailDTO对象并调用sendEmail方法实现
     *
     * @param to 收件人邮箱地址
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param attachments 附件文件路径列表,每个元素代表一个附件的完整路径
     * @return 返回Result对象,包含操作结果信息
     */
    public Result<String> sendAttachmentEmail(String to, String subject, String content, List<String> attachments) {
        // 创建EmailDTO对象并设置基本属性
        EmailDTO dto = new EmailDTO();
        dto.setTo(to);              // 设置收件人
        dto.setSubject(subject);    // 设置邮件主题
        dto.setContent(content);    // 设置邮件内容
        dto.setHtml(false);         // 设置邮件格式为纯文本
        dto.setAttachments(attachments); // 设置附件列表
        return sendEmail(dto); // 调用sendEmail方法发送邮件并返回结果
    }

    /**
     * 发送Thymeleaf模板邮件(简化版)
     * 该方法为发送模板邮件的简化版本,封装了邮件的基本信息
     *
     * @param to 收件人邮箱地址
     * @param subject 邮件主题
     * @param templateName Thymeleaf模板名称
     * @param templateData 模板所需的数据对象
     * @return 返回操作结果,包含邮件发送状态信息
     */
    public Result<String> sendTemplateEmail(String to, String subject, String templateName, Object templateData) {
        EmailDTO dto = new EmailDTO();      // 创建邮件数据传输对象
        dto.setTo(to);                      // 设置收件人邮箱
        dto.setSubject(subject);            // 设置邮件主题
        dto.setTemplateName(templateName);  // 设置模板名称
        dto.setTemplateData(templateData);  // 设置模板数据
        return sendEmail(dto);              // 调用邮件发送方法并返回结果
    }
}

2.3 模板邮件

在 resources 下新建 templates 目录,并将 html 文件放置其中。

<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>模板邮件示例</title>
    <style>
        .container { width: 80%; margin: 0 auto; padding: 20px; border: 1px solid #eee; border-radius: 8px; }
        .title { color: #2c3e50; font-size: 18px; font-weight: bold; margin-bottom: 15px; }
        .content { color: #34495e; line-height: 1.8; margin-bottom: 20px; }
        .footer { color: #95a5a6; font-size: 14px; text-align: center; margin-top: 30px; }
    </style>
</head>
<body>
<div class="container">
    <div class="title" th:text="${title}">邮件标题</div>
    <div class="content">
        <p>尊敬的用户:</p>
        <p>您好!您的<span th:text="${bizType}">业务名称</span>已处理完成,详情如下:</p>
        <ul>
            <li>业务ID:<span th:text="${bizId}">123456</span></li>
            <li>处理时间:<span th:text="${handleTime}">2025-11-16 10:00:00</span></li>
            <li>处理结果:<span style="color: #27ae60; font-weight: bold;" th:text="${result}">成功</span></li>
        </ul>
        <p>如有疑问,请联系客服!</p>
    </div>
    <div class="footer">© 2025 </div>
</div>
</body>
</html>

2.4 使用示例

/**
 * 邮件发送测试接口(本地启动后,通过浏览器/Postman调用)
 */
@Slf4j
@RestController
@RequestMapping("/email/test")
@RequiredArgsConstructor
public class EmailTestController {

    @Resource
    private final EmailService emailService;

    /**
     * 测试1:发送普通文本邮件
     * 访问地址:http://localhost:8080/email/test/sendText
     */
    @GetMapping("/sendText")
    public Result<String> sendTextEmail() {
        return emailService.sendTextEmail(
                "1979774006@qq.com",  // 收件人
                "普通文本邮件测试",    // 主题
                "这是Spring Boot发送的普通文本邮件,无需复杂配置,简单易用!"  // 内容
        );
    }

    /**
     * 测试2:发送HTML邮件
     * 访问地址:http://localhost:8080/email/test/sendHtml
     */
    @GetMapping("/sendHtml")
    public Result<String> sendHtmlEmail() {
        String htmlContent = """
                <h3>HTML邮件测试</h3>
                <p>这是一封<span style="color: red; font-weight: bold;">HTML格式</span>的邮件</p>
                <p>支持:<br/>
                1. 字体样式(颜色、大小、加粗)<br/>
                2. 列表<br/>
                3. 链接:<a href="https://www.baidu.com">百度一下</a><br/>
                </p>
                """;
        return emailService.sendHtmlEmail(
                "1979774006@qq.com",
                "HTML邮件测试",
                htmlContent
        );
    }

    /**
     * 测试3:发送带附件的邮件
     * 访问地址:http://localhost:8080/email/test/sendAttachment
     */
    @GetMapping("/sendAttachment")
    public Result<String> sendAttachmentEmail() {
        // 附件本地路径(替换为你的文件路径,如 D:/test.txt、/Users/xxx/test.pdf)
        // 获取resources路径
        String filePath = this.getClass().getResource("/file").getPath();

        log.info("filePath: {}", filePath);
        String attachmentPath1 = filePath + "/test.txt";
        String attachmentPath2 = filePath +"/test.pdf";
        return emailService.sendAttachmentEmail(
                "1979774006@qq.com",
                "带附件邮件测试",
                "这封邮件包含2个附件,请注意查收!",
                Arrays.asList(attachmentPath1, attachmentPath2)
        );
    }

    /**
     * 测试4:发送Thymeleaf模板邮件
     * 访问地址:http://localhost:8080/email/test/sendTemplate
     */
    @GetMapping("/sendTemplate")
    public Result<String> sendTemplateEmail() {
        // 模板参数(与模板文件中的 ${xxx} 对应)
        Map<String, Object> templateData = new HashMap<>();
        templateData.put("title", "模板邮件测试");
        templateData.put("bizType", "用户注册验证");
        templateData.put("bizId", "REG20251116001");
        templateData.put("handleTime", "2025-11-16 15:30:00");
        templateData.put("result", "验证成功");

        return emailService.sendTemplateEmail(
                "1979774006@qq.com",
                "模板邮件测试",
                "email-template",  // 模板文件名(无需后缀.html)
                templateData       // 模板参数
        );
    }
}

到这一个简单的邮件发送服务就实现完成了,我们也可以对他进行一些扩展操作。

三、功能扩展

3.1 异步发送邮件(避免阻塞主线程)

第一步:在启动类添加 @EnableAsync 注解:

@SpringBootApplication
@EnableAsync // 开启异步支持
public class EmailApplication extends BaseDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(EmailApplication.class, args);
    }
}

第二步:在邮件发送方法上添加 @Async 注解:

@Async // 异步发送
public BaseResponse<String> sendTextEmail(String to, String subject, String content) {
    // 原方法逻辑不变
}

或者在发送调用方法时,开启一个新的线程去执行业务方法!

3.2 抄送 / 密送功能

第一步:修改 EmailDTO 新增字段:

@Data
public class EmailDTO {
    // 原有字段...
    private String cc;  // 抄送(多个用逗号分隔)
    private String bcc; // 密送(多个用逗号分隔)
}

第二步:在 EmailService.sendEmail 方法中添加:

// 抄送
if (Objects.nonNull(emailDTO.getCc())) {
    helper.setCc(emailDTO.getCc().split(","));
}
// 密送
if (Objects.nonNull(emailDTO.getBcc())) {
    helper.setBcc(emailDTO.getBcc().split(","));
}