【Email】基于SpringBoot3.4.x集成发送邮件功能
摘要
在业务系统开发过程中,经常有发送邮件的需求,希望可以系统在某个时候自动触发邮件,或者支持用户主动触发邮件,Spring Boot
框架提供了发送邮件的功能,通过简单的配置即可实现。
有时候不仅要发送邮件,对邮件内容格式、样式还有更高要求,我们也可以利用模板引擎来实现,比如Thymeleaf
,Freemarker
等等。
本着"最新 "原则,这篇文章依然采用最新版的Spring Boot 3.4.0
,OpenJDK
最新LTS
版21
,IntelliJ IDEA
最新社区版2024.3.1
。
本地开发环境说明
开发用到的主要框架、工具版本如下
开发依赖 | 版本 |
---|---|
Spring Boot | 3.4.0 |
spring-boot-starter-mail | 3.4.0 |
spring-boot-starter-thymeleaf | 3.4.0 |
JDK | 21 |
IntelliJ IDEA | 2024.3.1 |
工程目录结构大致如下
pom.xml
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
启动类
java
package com.wen3.demo.email;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author: tangheng
*/
@SpringBootApplication
public class EmailDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EmailDemoApplication.class, args);
}
}
application.yaml
yaml
spring:
mail:
host: "smtp.sina.com" # 发件服务器地址,不同邮件平台地址不同
port: 25 #常用邮件端口25、109、110、143、465、995、993、994 如果开启了SSL安全则使用对应的端口号,25为非加密端口号
username: xxxx@sina.com #发送邮件的账号
password: xxxxxxxxxxxxxxxx #发送邮件账号的授权码,这里的授权码不是验证码.需要到邮箱
default-encoding: utf-8 #设置编码
properties: # 设置邮件超时时间防止服务器阻塞
timeout: 5000
connection-timeout: 5000
write-timeout: 5000
mail:
smtp:
auth: true
starttls:
enable: true
required: true
写一个邮件模板
email-template.html
内容如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>[[${title}]]</title>
</head>
<body>
<div style="background-color:#ECECEC; padding: 15px;">
<table cellpadding="0" align="center"
style="width: 600px; margin: 0px auto; text-align: left; position: relative; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; font-size: 14px; font-family:微软雅黑, 黑体; line-height: 1.5; box-shadow: rgb(153, 153, 153) 0px 0px 5px; border-collapse: collapse; background-position: initial initial; background-repeat: initial initial;background:#fff;">
<tbody>
<tr>
<th valign="middle"
style="height: 25px; line-height: 25px; padding: 15px 35px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #42a3d3; background-color: #49bcff; border-top-left-radius: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px;">
<font face="微软雅黑" size="5" style="color: rgb(255, 255, 255); ">测试邮件</font>
</th>
</tr>
<tr>
<td>
<div style="padding:25px 35px 40px; background-color:#fff;">
<h2 style="margin: 5px 0px; ">
<font color="#333333" style="line-height: 20px; ">
<font style="line-height: 22px; " size="4">
亲爱的<b>[[${email}]]</b>用户,您好:</font>
</font>
</h2>
<p>首先感谢您加入【运维自助平台】!请在验证页面输入以下验证码:<br>
<b style="font-size: 18px">[[${code}]]</b><br>
本验证码5分钟内有效,为了保障您的账户安全,请勿向任何人提供此验证码。感谢您的支持!<br>
如果您有什么疑问可以联系管理员,Email: admin@xxx.com</p>
<p align="right">运维自助平台</p>
<p align="right">[[${date}]]</p>
<div style="width:700px;margin:0 auto;">
<div style="padding:10px 10px 0;border-top:1px solid #ccc;color:#747474;margin-bottom:20px;line-height:1.3em;font-size:12px;">
<p>本邮件系统自动发送,请勿回复!<br>
请保管好您的邮箱,避免账号被他人盗用
</p>
<p>©运维自助平台</p>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
定义模板引擎工具类
TemplateUtil.java
内容如下
java
package com.wen3.demo.email.template;
import cn.hutool.core.date.DateUtil;
import jakarta.annotation.Resource;
import lombok.AccessLevel;
import lombok.experimental.FieldDefaults;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.util.Date;
/**
* @author: tangheng
*/
@Service
@FieldDefaults(level = AccessLevel.PRIVATE)
public class TemplateUtil {
@Resource
private TemplateEngine templateEngine;
/**
* 获得验证码模板
*/
public String getCaptchaTempl(String email, String code) {
Context context = new Context();
//设置模板所需的参数
context.setVariable("title","验证码");
context.setVariable("email",email);
context.setVariable("code",code);
context.setVariable("date", DateUtil.format(new Date(),"yyyy-MM-dd hh:mm:ss"));
//通过模板类将动态参数传入HTML模板,并返回模板内容 参数一:模板名字,参数二:动态参数Web文本
String content = templateEngine.process("/email-template", context);
return content;
}
}
定义一个邮件发送对象
java
package com.wen3.demo.email.dto;
import lombok.*;
import lombok.experimental.Accessors;
import lombok.experimental.FieldDefaults;
import lombok.experimental.SuperBuilder;
import java.util.List;
/**
* 邮件发送对象
*
* @author: tangheng
*/
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@SuperBuilder
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class EMailCmd {
List<String> tos;
String subject;
String text;
}
封装一个邮件发送器
java
package com.wen3.demo.email.sender;
import com.wen3.demo.email.dto.EMailCmd;
import jakarta.annotation.Resource;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.AccessLevel;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
/**
* @author: tangheng
*/
@Slf4j
@Service
@FieldDefaults(level = AccessLevel.PRIVATE)
public class EmailSender {
@Resource
JavaMailSender mailSender;
public void sendEMail(EMailCmd eMailCmd) {
try {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage.setFrom("太空眼睛<best5721@sina.com>"); //设置发送邮件账号
simpleMailMessage.setTo(eMailCmd.getTos().toArray(String[]::new)); //设置接收邮件的人,可以多个
simpleMailMessage.setSubject(eMailCmd.getSubject()); //设置发送邮件的主题
simpleMailMessage.setText(eMailCmd.getText()); //设置发送邮件的内容
mailSender.send(simpleMailMessage);
} catch (MailException e) {
log.error("邮件发送失败!");
}
}
@SneakyThrows
public void sendHtmlEMail(EMailCmd eMailCmd) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper minehelper = new MimeMessageHelper(message, true);
minehelper.setFrom("太空眼睛<best5721@sina.com>");
// minehelper.setFrom("best5721@sina.com", "太空眼睛");
minehelper.setTo(eMailCmd.getTos().toArray(String[]::new)); //设置接收邮件的人,可以多个
minehelper.setSubject(eMailCmd.getSubject()); //设置发送邮件的主题
minehelper.setText(eMailCmd.getText(),true); //设置发送邮件的内容 第二个设置为true则可以发送带HTML的邮件
mailSender.send(message);
} catch (MessagingException e) {
log.error("邮件发送失败!");
}
}
}
邮件地址的格式可以写成
best5721@sina.com
也可以写成
太空眼睛<best5721@sina.com>
单元测试
邮件模板单元测试
java
package com.wen3.demo.email.template;
import cn.hutool.core.util.RandomUtil;
import com.wen3.demo.email.EmailDemoSpringBootTestBase;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class TemplateUtilTest extends EmailDemoSpringBootTestBase {
@Resource
TemplateUtil templateUtil;
@Test
void getCaptchaTempl() {
String email = "tangheng";
String code = RandomUtil.randomNumbers(5);
String testResult = templateUtil.getCaptchaTempl(email,code);
log.info("testResult: {}", testResult);
assertNotNull(testResult);
}
}
发送邮件单元测试
java
package com.wen3.demo.email.sender;
import cn.hutool.core.util.RandomUtil;
import com.wen3.demo.email.EmailDemoSpringBootTestBase;
import com.wen3.demo.email.dto.EMailCmd;
import com.wen3.demo.email.template.TemplateUtil;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class EmailSenderTest extends EmailDemoSpringBootTestBase {
@Resource
EmailSender emailSender;
@Resource
TemplateUtil templateUtil;
@Resource
MailProperties mailProperties;
@Test
void sendHtmlEMail() {
String to = "friendly1234@126.com";
EMailCmd eMailCmd = EMailCmd.builder()
.tos(List.of(to))
.subject("测试邮件")
.text(templateUtil.getCaptchaTempl(to, RandomUtil.randomNumbers(5)))
.build();
emailSender.sendHtmlEMail(eMailCmd);
}
}