Spring Boot 整合邮件发送实战:spring-boot-demo-email 模块全解析(文本 / HTML 模板 / 附件 / 静态资源四种邮件)
2026/9/19 14:22:48 网站建设 项目流程

Spring Boot 整合邮件发送实战:spring-boot-demo-email 模块全解析(文本 / HTML 模板 / 附件 / 静态资源四种邮件)

【免费下载链接】spring-boot-demo🚀一个用来深入学习并实战 Spring Boot 的项目。项目地址: https://gitcode.com/gh_mirrors/sp/spring-boot-demo

本篇文章以 spring-boot-demo 仓库中的 demo-email 模块为主线,完整讲解 Spring Boot 如何整合 JavaMail 发送邮件。该模块覆盖了生产环境中最常见的四种邮件形态:简单文本邮件、HTML 邮件(含 Thymeleaf 模板渲染与自定义模板目录)、带附件邮件、正文内嵌静态资源(图片)邮件,并引入 jasypt 对邮箱密码进行加密存储。阅读本文后,你将掌握 Spring Boot 邮件发送的完整配置、JavaMailSender底层 API 的使用方法,以及一套可直接复制到业务项目中的邮件服务封装方案。

一、模块定位与整体架构

demo-email是 spring-boot-demo 系列实战项目中的一个独立 Maven 模块,其核心目标是演示Spring Boot 与 JavaMail(SMTP)的整合。模块结构非常清晰,遵循"接口 + 实现 + 测试"的经典分层:

demo-email/ ├── pom.xml # 依赖管理 └── src/ ├── main/ │ ├── java/com/xkcoding/email/ │ │ ├── SpringBootDemoEmailApplication.java # 启动类 │ │ └── service/ │ │ ├── MailService.java # 邮件服务接口 │ │ └── impl/MailServiceImpl.java # 邮件服务实现 │ └── resources/ │ ├── application.yml # 邮件与 jasypt 配置 │ ├── templates/welcome.html # Thymeleaf 邮件模板(默认目录) │ ├── email/test.html # 自定义目录下的邮件模板 │ └── static/xkcoding.png # 附件/静态资源测试图片 └── test/java/com/xkcoding/email/ ├── SpringBootDemoEmailApplicationTests.java # 上下文加载测试基类 ├── PasswordTest.java # jasypt 密码加解密测试 └── service/MailServiceTest.java # 四种邮件发送测试

从整体调用链看:业务方调用 MailService 接口,由 MailServiceImpl 内部注入 Spring Boot 自动配置好的JavaMailSender完成实际发送,邮件模板则由 Thymeleaf 的TemplateEngine渲染为 HTML 字符串后传入。启动类 SpringBootDemoEmailApplication 是一个标准的@SpringBootApplication,通过SpringApplication.run启动,邮件相关 Bean 均由 Spring Boot 的自动配置机制装配。

二、环境准备:pom.xml 依赖解析

模块的依赖声明位于 demo-email/pom.xml,父工程为spring-boot-democom.xkcoding组织),Java 版本 1.8。核心依赖及其作用如下:

依赖说明
spring-boot-starter-mailSpring Boot 邮件官方 Starter,内含 Spring 的spring-context-support与 JavaMail API,自动配置JavaMailSender/JavaMailSenderImpl
jasypt-spring-boot-starter(2.1.1)配置文件加密组件,用于对spring.mail.password进行ENC(...)加密存储与运行时解密
spring-boot-starter-thymeleaf模板引擎,用于渲染 HTML 邮件正文,配合templateEngine.process("模板名", context)使用
spring-boot-starter-test(scope=test)提供 JUnit、Spring Test 等测试基础设施
hutool-all工具类库,此处主要使用ArrayUtil.isNotEmpty判断抄送数组、ResourceUtil.getResource获取 classpath 资源
<dependencies> <!-- Spring Boot 邮件依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <!--jasypt配置文件加解密--> <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>${jasypt.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> </dependency> <!-- Spring Boot 模板依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> </dependencies>

构建配置中设置了finalNamespring-boot-demo-email,并引入spring-boot-maven-plugin用于打包可执行 Jar。

三、核心配置:application.yml 全参数解析

邮件相关的全部配置集中在 demo-email/src/main/resources/application.yml:

spring: mail: host: smtp.mxhichina.com port: 465 username: spring-boot-demo@xkcoding.com # 使用 jasypt 加密密码,使用com.xkcoding.email.PasswordTest.testGeneratePassword 生成加密密码,替换 ENC(加密密码) password: ENC(OT0qGOpXrr1Iog1W+fjOiIDCJdBjHyhy) protocol: smtp test-connection: true default-encoding: UTF-8 properties: mail.smtp.auth: true mail.smtp.starttls.enable: true mail.smtp.starttls.required: true mail.smtp.ssl.enable: true mail.display.sendmail: spring-boot-demo # 为 jasypt 配置解密秘钥 jasypt: encryptor: password: spring-boot-demo

3.1 核心参数说明

配置项示例值作用与注意点
spring.mail.hostsmtp.mxhichina.comSMTP 服务器地址,需根据邮件服务商替换(如 QQ 邮箱smtp.qq.com、163 邮箱smtp.163.com
spring.mail.port465SMTP 端口。465 对应 SSL 加密端口;若走 STARTTLS 通常使用 587;普通 25 端口常被云厂商封禁,生产建议 465/587
spring.mail.username邮箱账号发送方邮箱地址,同时会被MailServiceImpl通过@Value("${spring.mail.username}")注入作为from(发件人)
spring.mail.passwordENC(...)邮箱授权码或密码,此处以 jasypt 加密密文形式给出,运行时解密后再用于 SMTP 认证
spring.mail.protocolsmtp邮件传输协议,默认即 smtp
spring.mail.test-connectiontrue是否在应用启动时调用JavaMailSenderImpl.testConnection()校验 SMTP 连接可用性。为true时若连接失败会直接导致启动失败,便于尽早发现配置错误
spring.mail.default-encodingUTF-8邮件内容的默认字符编码,避免中文乱码

3.2 properties 中的 SMTP 细节参数

spring.mail.properties下的mail.smtp.*会被透传给 JavaMail 的Session属性,直接影响与 SMTP 服务器的握手行为:

  • mail.smtp.auth: true:开启 SMTP 认证,发送前必须先登录(绝大多数服务商强制要求);
  • mail.smtp.starttls.enable: true:启用 STARTTLS,允许在明文连接上升级为 TLS 加密通道;
  • mail.smtp.starttls.required: true:强制要求服务器支持 STARTTLS,否则拒绝连接;
  • mail.smtp.ssl.enable: true:直接以 SSL 加密方式建立连接(配合 465 端口使用)。本示例同时开启了 SSL 与 STARTTLS 配置,在实际使用时建议与端口匹配:465 端口配ssl.enable=true,587 端口配starttls.enable=true,避免重复协商;
  • mail.display.sendmail: spring-boot-demo:邮件头的显示名称(X-Mailer等信息,部分服务商用于标识发信应用)。

3.3 jasypt 解密密钥

jasypt: encryptor: password: spring-boot-demo

jasypt-spring-boot-starter会在应用启动时读取jasypt.encryptor.password作为加解密密钥,凡是配置文件中形如ENC(密文)的值都会被自动解密为明文后再注入到 Spring 环境中。注意:该密钥属于安全敏感信息,本示例为演示写在 yml 中;生产环境建议通过环境变量、启动参数等方式注入,避免明文入库。

四、服务接口设计:MailService

MailService 定义了四种发送能力的统一接口,所有方法都支持可变参数cc(抄送地址,可传多个或省略):

public interface MailService { /** * 发送文本邮件 * * @param to 收件人地址 * @param subject 邮件主题 * @param content 邮件内容 * @param cc 抄送地址 */ void sendSimpleMail(String to, String subject, String content, String... cc); /** * 发送HTML邮件 * * @param to 收件人地址 * @param subject 邮件主题 * @param content 邮件内容 * @param cc 抄送地址 * @throws MessagingException 邮件发送异常 */ void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException; /** * 发送带附件的邮件 * * @param to 收件人地址 * @param subject 邮件主题 * @param content 邮件内容 * @param filePath 附件地址 * @param cc 抄送地址 * @throws MessagingException 邮件发送异常 */ void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) throws MessagingException; /** * 发送正文中有静态资源的邮件 * * @param to 收件人地址 * @param subject 邮件主题 * @param content 邮件内容 * @param rscPath 静态资源地址 * @param rscId 静态资源id * @param cc 抄送地址 * @throws MessagingException 邮件发送异常 */ void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) throws MessagingException; }

这种"接口 + 实现"的封装方式便于在业务中解耦:上层只依赖MailService,后续若更换邮件服务商或引入队列异步发送,只需替换实现类。

五、实现剖析:MailServiceImpl 与 JavaMailSender 底层原理

MailServiceImpl 是整个模块的核心,它通过@Autowired注入JavaMailSender,并用@Value("${spring.mail.username}")注入发件人地址:

@Service public class MailServiceImpl implements MailService { @Autowired private JavaMailSender mailSender; @Value("${spring.mail.username}") private String from; // ... 四种发送方法实现 }

5.1 简单文本邮件:SimpleMailMessage

@Override public void sendSimpleMail(String to, String subject, String content, String... cc) { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom(from); message.setTo(to); message.setSubject(subject); message.setText(content); if (ArrayUtil.isNotEmpty(cc)) { message.setCc(cc); } mailSender.send(message); }

SimpleMailMessage是 Spring 对纯文本邮件的轻量封装,直接设置发件人、收件人、主题、正文后交由mailSender.send()发送。当cc非空时通过setCc(cc)批量设置抄送人(可变参数天然支持多个抄送地址)。

5.2 HTML 邮件:MimeMessage + MimeMessageHelper

@Override public void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(content, true); if (ArrayUtil.isNotEmpty(cc)) { helper.setCc(cc); } mailSender.send(message); }

HTML 邮件必须走 MIME 协议:先通过mailSender.createMimeMessage()创建MimeMessage,再以new MimeMessageHelper(message, true)包装——第二个参数true表示启用 multipart 模式,这是后续支持 HTML 正文、附件和内嵌资源的前提。关键点是helper.setText(content, true)的第二个布尔参数true代表将正文按 HTML 解析渲染,若传false则 HTML 标签会原样显示为文本。

5.3 附件邮件:addAttachment

@Override public void sendAttachmentsMail(String to, String subject, String content, String filePath, String... cc) throws MessagingException { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(content, true); if (ArrayUtil.isNotEmpty(cc)) { helper.setCc(cc); } FileSystemResource file = new FileSystemResource(new File(filePath)); String fileName = filePath.substring(filePath.lastIndexOf(File.separator)); helper.addAttachment(fileName, file); mailSender.send(message); }

附件通过helper.addAttachment(fileName, file)挂载。实现中有两个值得注意的细节:

  1. FileSystemResource将本地文件路径包装为 Spring 的Resource抽象,供邮件组件读取二进制流;
  2. 文件名取自filePath.substring(filePath.lastIndexOf(File.separator)),即从路径最后一个/(Linux)或\(Windows)之后截取,避免附件名带完整路径。这里的File.separator保证了跨平台兼容性。

5.4 内嵌静态资源邮件:addInline 与 cid 协议

@Override public void sendResourceMail(String to, String subject, String content, String rscPath, String rscId, String... cc) throws MessagingException { MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(content, true); if (ArrayUtil.isNotEmpty(cc)) { helper.setCc(cc); } FileSystemResource res = new FileSystemResource(new File(rscPath)); helper.addInline(rscId, res); mailSender.send(message); }

静态资源(典型场景是邮件中的 logo、产品图)通过helper.addInline(rscId, res)作为inline 附件嵌入 MIME 结构,正文中通过cid:rscId引用:

<img src="cid:xkcoding" />

这种"content-id 引用"方式让图片随邮件一起发送,收件人无需外网也能看到,避免了引用外部 URL 时被邮件客户端拦截或图片失效的问题。测试用例sendResourceMail中的正文即为此用法(见 MailServiceTest)。

六、密码安全:jasypt 加解密实战

邮件密码属于敏感凭据,直接明文写在application.yml中有泄露风险。本模块通过 jasypt 将密码加密为ENC(...)密文。加密密码的生成方式见 PasswordTest:

public class PasswordTest extends SpringBootDemoEmailApplicationTests { @Autowired private StringEncryptor encryptor; /** * 生成加密密码 */ @Test public void testGeneratePassword() { // 你的邮箱密码 String password = "Just4Test!"; // 加密后的密码(注意:配置上去的时候需要加 ENC(加密密码)) String encryptPassword = encryptor.encrypt(password); String decryptPassword = encryptor.decrypt(encryptPassword); System.out.println("password = " + password); System.out.println("encryptPassword = " + encryptPassword); System.out.println("decryptPassword = " + decryptPassword); } }

运行该测试,控制台会同时打印明文、encrypt()加密结果和decrypt()解密结果。将加密结果按ENC(加密密码)的格式替换到application.ymlspring.mail.password中即可。运行时 jasypt 依据jasypt.encryptor.password解密出真实密码再交给 JavaMail 做 SMTP 认证。加密算法默认使用 PBEWithMD5AndDES 等 jasypt 内置算法,具体由jasypt-spring-boot-starter版本决定,本项目固定版本为 2.1.1(见 demo-email/pom.xml 中<jasypt.version>2.1.1</jasypt.version>)。

七、模板化 HTML 邮件:Thymeleaf 渲染

7.1 默认模板目录:templates/welcome.html

spring-boot-starter-thymeleaf默认将classpath:/templates/作为模板根目录、.html作为后缀。邮件模板 welcome.html 位于该目录,通过th:text绑定变量、th:href绑定链接:

<div id="welcome"> <h3>欢迎使用 <span th:text="${project}"></span> - Powered By <span th:text=" ${author}"></span></h3> <span th:text="${url}"></span> <div style="text-align: center; padding: 10px"> <a style="text-decoration: none;" href="#" th:href="@{${url}}" target="_bank"> <strong>spring-boot-demo,入门Spring Boot的首选Demo!:)</strong> </a> </div> ... </div>

配合测试方法sendHtmlMail,先用TemplateEngine渲染模板再发送:

@Test public void sendHtmlMail() throws MessagingException { Context context = new Context(); context.setVariable("project", "Spring Boot Demo"); context.setVariable("author", "Yangkai.Shen"); context.setVariable("url", "https://github.com/xkcoding/spring-boot-demo"); String emailTemplate = templateEngine.process("welcome", context); mailService.sendHtmlMail("237497819@qq.com", "这是一封模板HTML邮件", emailTemplate); }

流程为:创建 Thymeleaf 的ContextsetVariable注入模板变量 →templateEngine.process("welcome", context)渲染出 HTML 字符串 → 交给sendHtmlMail发送。这样邮件正文与 Java 代码完全分离,业务上修改活动邮件样式无需改动代码。

7.2 自定义模板目录:SpringResourceTemplateResolver

当邮件模板不想放在默认的templates/目录时,可通过SpringResourceTemplateResolver自定义前缀/后缀。测试方法sendHtmlMail2演示了这一点,它指向classpath:/email/目录下的 test.html:

@Test public void sendHtmlMail2() throws MessagingException { SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); templateResolver.setApplicationContext(context); templateResolver.setCacheable(false); templateResolver.setPrefix("classpath:/email/"); templateResolver.setSuffix(".html"); templateEngine.setTemplateResolver(templateResolver); Context context = new Context(); context.setVariable("project", "Spring Boot Demo"); context.setVariable("author", "Yangkai.Shen"); context.setVariable("url", "https://github.com/xkcoding/spring-boot-demo"); String emailTemplate = templateEngine.process("test", context); mailService.sendHtmlMail("237497819@qq.com", "这是一封模板HTML邮件", emailTemplate); }

三个关键设置:

  • setApplicationContext(context):把 Spring 的ApplicationContext交给解析器,使其能解析classpath:前缀资源;
  • setPrefix("classpath:/email/")setSuffix(".html"):将模板目录重定向到classpath:/email/,此时process("test", context)实际渲染classpath:/email/test.html
  • setCacheable(false):关闭模板缓存,便于开发期修改模板即时生效(生产环境建议开启缓存提升性能)。

该目录下的 test.html 与welcome.html内容结构一致,同样通过th:text渲染projectauthorurl三个变量。

八、端到端验证:测试用例总览

测试基类 SpringBootDemoEmailApplicationTests 使用@RunWith(SpringRunner.class) + @SpringBootTest加载完整 Spring 上下文。在其之上,MailServiceTest 注入了MailService、Thymeleaf 的TemplateEngine以及ApplicationContext,包含 5 个测试方法:

测试方法验证内容关键点
sendSimpleMail简单文本邮件纯文本正文直接发送
sendHtmlMail模板 HTML 邮件templateEngine.process("welcome", context)渲染默认目录模板
sendHtmlMail2自定义模板目录SpringResourceTemplateResolver重定向到classpath:/email/
sendAttachmentsMail附件邮件ResourceUtil.getResource("static/xkcoding.png")获取 classpath 内图片作为附件
sendResourceMail静态资源邮件正文<img src='cid:xkcoding'>+helper.addInline("xkcoding", res)

附件与静态资源测试均使用模块自带的 static/xkcoding.png(100x100 像素)作为测试素材,通过 hutool 的ResourceUtil.getResource("static/xkcoding.png")从 classpath 定位文件绝对路径,再传入发送方法。

九、运行与使用流程

  1. 克隆并进入模块:在仓库根目录执行git clone后进入demo-email目录,或直接在父工程spring-boot-demo下构建该模块;
  2. 修改配置:编辑 application.yml,将spring.mail.hostspring.mail.portspring.mail.username替换为你自己的邮箱服务商参数;
  3. 生成加密密码:运行PasswordTest.testGeneratePassword,将输出结果按ENC(密文)格式写入spring.mail.password
  4. 修改收件人:将MailServiceTest各测试方法中的收件人地址237497819@qq.com替换为实际测试邮箱;
  5. 执行测试:运行mvn test或直接运行各@Test方法,即可收到对应的文本、HTML、附件与内嵌图片邮件。

注意:spring.mail.test-connection: true会在应用启动时校验 SMTP 连接,若账号密码或端口配置有误,启动阶段即会抛出MailAuthenticationException或连接异常,可据此快速定位问题。

十、技术要点回顾

  • 四种邮件形态的本质区别:文本邮件用SimpleMailMessage;HTML / 附件 / 内嵌资源都必须使用MimeMessage + MimeMessageHelper(message, true)的 multipart 模式,分别依赖setText(content, true)addAttachmentaddInline(rscId, res)
  • 内嵌图片用cid:协议helper.addInline(rscId, res)与正文<img src="cid:rscId">一一对应,实现图片随信发送;
  • 密码安全:jasypt 的ENC()密文 + 独立密钥,避免明文凭据进仓库;密钥本身在生成环境需独立保管;
  • 模板与代码解耦:Thymeleaf 支持默认templates/目录,也支持通过SpringResourceTemplateResolver自由定制模板目录与缓存策略;
  • 抄送批量支持:四个接口方法的String... cc可变参数统一支持多抄送场景。

如需深入阅读源码,可从 MailService 与 MailServiceImpl 入手,配合 MailServiceTest 五组测试用例对照学习;本模块同时是 spring-boot-demo 仓库中"邮件 + 模板引擎 + 配置加密"组合使用的代表性示例。

【免费下载链接】spring-boot-demo🚀一个用来深入学习并实战 Spring Boot 的项目。项目地址: https://gitcode.com/gh_mirrors/sp/spring-boot-demo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询