当前位置: 首页 > news >正文

SpringBoot 发送邮件

文章目录

    • 1 快速 Starter
    • 2 结构
      • 1_邮件消息接口
      • 2_邮件发送者
    • 3 发送复杂邮件
    • 4 延迟创建邮件消息
    • 5 收件人 / 抄送 / 密送
    • 6 使用模版发送邮件
    • 7 扩展工具类封装
    • 8 总结

在项目的维护过程中,我们通常会在应用中加入短信或者邮件预警功能,比如当应用出现异常宕机时应该及时地将预警信息发送给运维或者开发人员,本文将介绍如何在 SpringBoot 中发送邮件。

在 SpringBoot 中发送邮件使用的是 Spring 提供的 org.springframework.mail.javamail.JavaMailSender,其提供了许多简单易用的方法,可发送简单的邮件、HTML 格式的邮件、带附件的邮件,还可以创建邮件模板。

1 快速 Starter

引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId>
</dependency>

配置邮箱服务,以 QQ 邮箱为例:

spring:application:name: mailmail:host: smtp.qq.com # SMTP 服务器主机port: 465 # SMTP 服务器端口username: xxxxxxxx@qq.com # SMTP 服务器的登录用户password: xxxxxxxxxxx # SMTP 服务器的登录密码,不是用户密码,在对应邮箱服务的安全中心中查看protocol: smtps # SMTP 服务器使用的协议properties:mail: # 其他 JavaMail 会话属性,传递给 JavaMail 底层实现smtp:auth: true # 启用 SMTP 身份认证starttls:enable: true #升级 TLS 握手ssl:enable: true # 开启 ssl
# mail中的属性是给 JavaMail 使用的,不是SpringBoot# https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html

发送简单邮件的示例代码

@Service
public class MailService {@Resourceprivate JavaMailSender mailSender;@Resourceprivate MailProperties mailProperties;public void sendSimpleMail(String to, String subject, String content) {SimpleMailMessage message = new SimpleMailMessage();message.setFrom(mailProperties.getUsername());message.setTo(to);message.setSubject(subject);message.setText(content);mailSender.send(message);}
}

测试:

@SpringBootTest
class Mail2ApplicationTests {@ResourceMailService mailService;@Testvoid contextLoads() {mailService.sendSimpleMail("xxxxxxx@qq.com","测试主题","这只是一个测试");}
}

测试结果:

2 结构

Spring Framework 中的 org.springframework.mail 包提供统一的邮件发送相关接口。

主要有两类:一类用于定义邮件消息结构、另一类用于发送邮件消息。

1_邮件消息接口

快速开始中使用的是 SimpleMailMessage(简单邮件消息实现)。

所有邮件消息类型统一实现了接口 MailMessage:

public interface MailMessage {// 发送人void setFrom(String from) throws MailParseException;// 收件地址: 收件人点击‘回复’按钮时,邮件应当发给哪个地址// 默认回复给 fromvoid setReplyTo(String replyTo) throws MailParseException;// 收件人void setTo(String to) throws MailParseException;// 支持多个收件人void setTo(String... to) throws MailParseException;// 抄送人void setCc(String cc) throws MailParseException;// 支持多个抄送人void setCc(String... cc) throws MailParseException;// 密送人void setBcc(String bcc) throws MailParseException;// 支持多个密送人void setBcc(String... bcc) throws MailParseException;// 发送邮箱的时间戳void setSentDate(Date sentDate) throws MailParseException;// 主题,邮箱标题void setSubject(String subject) throws MailParseException;// 发送数据void setText(String text) throws MailParseException;
}

MailMessage 默认提供了两个实现类,其中 MimeMailMessage 是复杂邮件消息类型:

2_邮件发送者

邮件发送接口主要有两个:

  • MailSender 是最基础接口,可发送简单文本邮件 (SimpleMailMessage)。
  • JavaMailSender 继承自 MailSender,支持 MIME 消息(如 HTML、附件、内嵌资源),典型使用方式结合 MimeMessageHelper 构造复杂邮件结构。
  • JavaMailSender 还支持 MimeMessagePreparator 回调机制,延迟创建复杂的 MimeMessage 。

MailSender 接口:

public interface MailSender {default void send(SimpleMailMessage simpleMessage) throws MailException {send(new SimpleMailMessage[] {simpleMessage});}// 可以发送多条消息void send(SimpleMailMessage... simpleMessages) throws MailException;}

JavaMailSender 接口:

public interface JavaMailSender extends MailSender {MimeMessage createMimeMessage();MimeMessage createMimeMessage(InputStream contentStream) throws MailException;default void send(MimeMessage mimeMessage) throws MailException {send(new MimeMessage[] {mimeMessage});}// 支持接收多个消息参数void send(MimeMessage... mimeMessages) throws MailException;.......
}

MimeMessagePreparator 接口:

@FunctionalInterface
public interface MimeMessagePreparator {void prepare(MimeMessage mimeMessage) throws Exception;
}

3 发送复杂邮件

发送复杂邮件(如带 HTML、附件、内嵌图片、多个收件人等),需要使用 JavaMailSender 的 MimeMessage + MimeMessageHelper 方式。

发送 HTML正文 + 附件 + 内嵌图片 示例:

public void sendComplexMail(String to) throws MessagingException {MimeMessage message = mailSender.createMimeMessage();// 第二个参数 multipart=true 代表该邮件是 multipart 结构MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");helper.setFrom(mailProperties.getUsername());helper.setTo(to);helper.setSubject("复杂邮件示例(含HTML、附件、内嵌图片)");// 构造 HTML 内容,注意内嵌图片使用 cid:xxx 标识String html = "<html><body>" +"<h2 style='color:blue;'>您好,这是HTML格式邮件</h2>" +"<p>请查看附件,也可以看到下面的图片:</p>" +"<img src='cid:logoImage'/>" +"</body></html>";helper.setText(html, true); // 第二个参数为 true 表示内容为 HTML// 添加附件(FileSystemResource 可以从磁盘读取)ClassPathResource file = new ClassPathResource("test.pdf");helper.addAttachment(Objects.requireNonNull(file.getFilename()), file);// 添加内嵌图片(resourceName 要与上面的 cid:xxx 匹配)ClassPathResource image = new ClassPathResource("logo.png");helper.addInline("logoImage", image);mailSender.send(message);
}

在 HTML 邮件中嵌入静态资源的过程和传入附件类似,唯一的区别在于需要标识资源的 cid

最终结果如下:

cid(Content-ID)是 MIME 协议中用于标识邮件中某个内嵌资源(通常是图片)的标记,用于在 HTML 中引用该资源。

4 延迟创建邮件消息

之前提到过 JavaMailSender 有一种更灵活发送邮件的方式 —— 使用 MimeMessagePreparator 接口。

即:允许在“需要时”才构造并配置 MimeMessage,而不是立即创建并设置,从而实现“延迟构造”和“回调式”的邮件发送逻辑。

其实就是利用了函数式接口的特性:

MimeMessagePreparator preparator = mimeMessage -> {MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);helper.setFrom(from);helper.setTo("XXXXXXX@qq.com");helper.setSubject("测试邮件");helper.setText("<html><body><h1>Hello</h1></body></html>", true);helper.addInline("logoImage", new ClassPathResource("logo.png"));
};
mailSender.send(preparator); // 由Spring在内部延迟构建并发送

如果不理解,可以对比一下之前发邮件的方式:

MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo("xxxxx@qq.com");
helper.setSubject("普通邮件");
helper.setText("Hello Email");
mailSender.send(message);
........

5 收件人 / 抄送 / 密送

先来理解一下这三个概念:

字段名关键词说明作用是否所有人能看到
收件人To正式接收邮件的人邮件是“发给他们”的
抄送Cc (Carbon Copy)次要接收人通知类收件人,“告知他们一份”
密送Bcc (Blind Carbon Copy)隐藏接收人悄悄通知某人

假设下面这一场景:

你是某公司主管,发送一封工作邮件给员工和老板。

有这几个角色,张三(你想让他执行某一任务,并且知晓李四收到了邮件)、李四(你想让他知情张三收到了邮件)、老板(你不想让张三李四知道老板也收到了邮件,同时让老板知晓张三、李四收到了邮件)。

那么就可以进行如下设置:

helper.setTo("zhangsan1@example.com", "zhangsan2@example.com");// 主收件人:张三
helper.setCc("lisi@example.com");// 抄送给李四
helper.setBcc("boss@example.com");// 密送给老板

6 使用模版发送邮件

这里使用 Thymeleaf 模板引擎来生成邮件内容并发送 HTML 邮件,Freemarker 类似。

首先引入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

添加如下配置文件:

spring:thymeleaf:prefix: classpath:/templates/suffix: .htmlmode: HTMLencoding: UTF-8

编写 HTML 邮件模版:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>激活邮件</title>
</head>
<body><h2>您好,<span th:text="${username}">先生/女士</span></h2><p>感谢注册,请点击下方链接激活您的账号:</p><p><a th:href="${activationUrl}" target="_blank">点击此处激活</a></p><hr><p>如果链接无法点击,请将以下地址复制到浏览器打开:</p><p th:text="${activationUrl}">https://example.com/activate</p>
</body>
</html>

编写邮箱服务类:

import jakarta.annotation.Resource;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;@Service
public class ThymeleafMailService {@Resourceprivate JavaMailSender mailSender;@Resourceprivate TemplateEngine templateEngine;@Value("${spring.mail.username}")private String from;public void sendActivationEmail(String toEmail, String username, String activationUrl) throws MessagingException {// 构建 MimeMessageMimeMessage message = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");helper.setFrom(from);helper.setTo(toEmail);helper.setSubject("账号激活邮件");// 1. 构建模板上下文Context context = new Context();context.setVariable("username", username);context.setVariable("activationUrl", activationUrl);// 2. 渲染模板String htmlContent = templateEngine.process("email-template", context);// 3. 设置 HTML 正文helper.setText(htmlContent, true); // 第二参数 true 表示 HTML// 4. 发送邮件mailSender.send(message);}
}

测试结果:

7 扩展工具类封装

通用的 Spring Boot 邮件发送工具类封装,支持:

  • 发送文本邮件
  • 发送 HTML 邮件
  • 添加附件
  • 添加内嵌图片
  • 多个收件人、抄送、密送

不需要的参数传 null 即可:

@Slf4j
@Component
public class MailUtil {@Resourceprivate JavaMailSender mailSender;@Value("${spring.mail.username}")private String from;/*** 发送复杂邮件(支持 HTML、附件、内嵌图片)** @param subject 邮件主题* @param content 邮件内容(HTML 可控)* @param to 收件人(多个)* @param cc 抄送人(可选)* @param bcc 密送人(可选)* @param attachments 附件路径 Map<显示名, 文件路径>,可为空* @param inlineImages 内嵌图片 Map<cid标识, 文件路径>,可为空* @param isHtml 是否HTML格式*/public void sendMail(String subject,String content,List<String> to,List<String> cc,List<String> bcc,Map<String, String> attachments,Map<String, String> inlineImages,boolean isHtml) {try {MimeMessage message = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");// 发件人从配置读取helper.setFrom(from);helper.setTo(to.toArray(new String[0]));if (cc != null && !cc.isEmpty()) {helper.setCc(cc.toArray(new String[0]));}if (bcc != null && !bcc.isEmpty()) {helper.setBcc(bcc.toArray(new String[0]));}helper.setSubject(subject);helper.setText(content, isHtml);// 添加附件if (attachments != null) {for (Map.Entry<String, String> entry : attachments.entrySet()) {File file = new File(entry.getValue());if (file.exists()) {helper.addAttachment(entry.getKey(), file);} else {log.warn("附件文件不存在: {}", file.getAbsolutePath());}}}// 添加内嵌图片if (inlineImages != null) {for (Map.Entry<String, String> entry : inlineImages.entrySet()) {File file = new File(entry.getValue());if (file.exists()) {helper.addInline(entry.getKey(), new FileSystemResource(file));} else {log.warn("内嵌图片文件不存在: {}", file.getAbsolutePath());}}}mailSender.send(message);log.info("邮件发送成功 -> {}", to);} catch (MessagingException e) {log.error("发送邮件失败:{}", e.getMessage(), e);}}
}

还是弄的不太方便,你可以自己尝试实现一个,哈哈 。

8 总结

邮件功能虽非核心业务,但在注册验证、通知提醒、工单系统等系统中却非常关键。

良好的邮件架构不仅能提升用户体验,也能让系统具备更强的扩展性和稳定性。

添加邮件日志记录、异步发送等能力,可继续在此基础上构建更完善的邮件服务模块。

http://www.lryc.cn/news/603083.html

相关文章:

  • 五自由度磁悬浮轴承转子不平衡质量的高性能控制策略全解析
  • 算法训练营day34 动态规划② 62.不同路径、63. 不同路径 II、343整数拆分、96.不同的二叉搜索树
  • Java响应式编程
  • ATF 运行时服务
  • ros2的package.xml和rosdep
  • 基于深度学习的医学图像分析:使用3D CNN实现肿瘤检测
  • 第十天:字符菱形
  • 一个Pycharm窗口添加多个项目来满足运行多个项目的需求
  • DDoS攻击防御:从5G到T级防护方案全对比
  • 企业级日志分析系统ELK
  • Python动态规划:从基础到高阶优化的全面指南(3)
  • 历史版本的vscode下载地址
  • 实验-静态路由
  • 智慧工地系统:科技赋能建筑新未来
  • 学习dify:一个开源的 LLM 应用开发平台
  • 【GitHub Workflows 基础(二)】深入理解 on、jobs、steps 的核心语法与执行逻辑
  • 【2025/07/28】GitHub 今日热门项目
  • 【iOS】类和分类的加载过程
  • LNMP架构+wordpress实现动静分离
  • Cacti RCE漏洞复现
  • 四、计算机组成原理——第1章:计算机系统概述
  • 可扩展架构模式——微服务架构最佳实践应该如何去做(方法篇)
  • 《汇编语言:基于X86处理器》第10章 结构和宏(2)
  • linux命令grep的实际应用
  • 在虚拟机ubuntu上修改framebuffer桌面不能显示图像
  • 1.vue体验
  • Android 媒体播放开发完全指南
  • Ansible提权sudo后执行报错
  • 电脑开机不显示网卡的原因
  • selenium 特殊场景处理