Spring Boot使用邮箱模板发送邮件完整指南
在现代Web应用开发中,邮件发送功能是必不可少的一部分。无论是用户注册验证、密码重置,还是营销推广,邮件都扮演着重要角色。本文将详细介绍如何在Spring Boot项目中使用邮箱模板发送精美的邮件。
前言
Spring Boot为邮件发送提供了强大的支持,通过集成Spring Mail和模板引擎(如Thymeleaf),我们可以轻松实现发送HTML格式的模板邮件。这种方式不仅让邮件内容更加美观,还能提高用户体验。
环境准备
1. 添加依赖
首先在pom.xml中添加必要的依赖:
<dependencies>
<!-- Spring Boot Starter Mail -->
<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>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
2. 邮箱配置
在application.yml或application.properties中配置邮箱信息:
spring:
mail:
host: smtp.qq.com # 邮箱服务器地址
port: 587 # 端口号
username: your-email@qq.com # 发送方邮箱
password: your-auth-code # 授权码(不是邮箱密码)
protocol: smtp
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
# Thymeleaf配置
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mode: HTML
encoding: UTF-8
cache: false
注意:
- QQ邮箱需要开启SMTP服务并获取授权码
- 不同邮箱服务商的配置可能有所不同
核心代码实现
1. 邮件服务接口
public interface EmailService {
/**
* 发送简单文本邮件
*/
void sendSimpleEmail(String to, String subject, String text);
/**
* 发送HTML模板邮件
*/
void sendTemplateEmail(String to, String subject, String templateName, Map<String, Object> variables);
/**
* 发送带附件的邮件
*/
void sendEmailWithAttachment(String to, String subject, String templateName,
Map<String, Object> variables, String attachmentPath);
}
2. 邮件服务实现类
@Service
@Slf4j
public class EmailServiceImpl implements EmailService {
@Autowired
private JavaMailSender mailSender;
@Autowired
private TemplateEngine templateEngine;
@Value("${spring.mail.username}")
private String from;
@Override
public void sendSimpleEmail(String to, String subject, String text) {
try {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
log.info("简单邮件发送成功,收件人:{}", to);
} catch (Exception e) {
log.error("简单邮件发送失败,收件人:{},错误信息:{}", to, e.getMessage());
throw new RuntimeException("邮件发送失败", e);
}
}
@Override
public void sendTemplateEmail(String to, String subject, String templateName, Map<String, Object> variables) {
try {
// 使用Thymeleaf渲染模板
Context context = new Context();
context.setVariables(variables);
String htmlContent = templateEngine.process(templateName, context);
// 创建邮件消息
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true); // true表示HTML格式
mailSender.send(message);
log.info("模板邮件发送成功,收件人:{},模板:{}", to, templateName);
} catch (Exception e) {
log.error("模板邮件发送失败,收件人:{},错误信息:{}", to, e.getMessage());
throw new RuntimeException("邮件发送失败", e);
}
}
@Override
public void sendEmailWithAttachment(String to, String subject, String templateName,
Map<String, Object> variables, String attachmentPath) {
try {
Context context = new Context();
context.setVariables(variables);
String htmlContent = templateEngine.process(templateName, context);
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(htmlContent, true);
// 添加附件
File file = new File(attachmentPath);
if (file.exists()) {
helper.addAttachment(file.getName(), file);
}
mailSender.send(message);
log.info("带附件邮件发送成功,收件人:{}", to);
} catch (Exception e) {
log.error("带附件邮件发送失败,收件人:{},错误信息:{}", to, e.getMessage());
throw new RuntimeException("邮件发送失败", e);
}
}
}
邮件模板设计
1. 用户注册验证邮件模板
在src/main/resources/templates目录下创建register-verify.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>账户验证</title>
<style>
body {
font-family: 'Arial', sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
.container {
max-width: 600px;
margin: 20px auto;
background: white;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.content {
padding: 40px 30px;
}
.btn {
display: inline-block;
background: #667eea;
color: white;
padding: 15px 30px;
text-decoration: none;
border-radius: 5px;
margin: 20px 0;
font-weight: bold;
}
.footer {
background: #f8f9fa;
padding: 20px;
text-align: center;
font-size: 12px;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>欢迎注册我们的服务!</h1>
</div>
<div class="content">
<p>亲爱的 <strong th:text="${username}">用户</strong>,</p>
<p>感谢您注册我们的服务!为了确保您的账户安全,请点击下面的按钮完成邮箱验证:</p>
<div style="text-align: center;">
<a th:href="${verifyUrl}" class="btn">验证邮箱</a>
</div>
<p>或者复制以下链接到浏览器地址栏:</p>
<p style="word-break: break-all; background: #f8f9fa; padding: 10px; border-radius: 5px;">
<span th:text="${verifyUrl}">验证链接</span>
</p>
<p><strong>注意:</strong>此验证链接将在 <span th:text="${expireTime}">24小时</span> 后失效。</p>
<p>如果您没有注册过我们的服务,请忽略此邮件。</p>
</div>
<div class="footer">
<p>此邮件由系统自动发送,请勿回复。</p>
<p>© 2024 Your Company. All rights reserved.</p>
</div>
</div>
</body>
</html>
2. 密码重置邮件模板
创建password-reset.html:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>密码重置</title>
<style>
body { font-family: Arial, sans-serif; background-color: #f4f4f4; }
.container { max-width: 600px; margin: 20px auto; background: white; border-radius: 10px; }
.header { background: #e74c3c; color: white; padding: 30px; text-align: center; }
.content { padding: 40px 30px; }
.btn { display: inline-block; background: #e74c3c; color: white; padding: 15px 30px;
text-decoration: none; border-radius: 5px; margin: 20px 0; }
.warning { background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 5px; margin: 20px 0; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔐 密码重置请求</h1>
</div>
<div class="content">
<p>您好,<strong th:text="${username}">用户</strong>!</p>
<p>我们收到了您的密码重置请求。如果这是您的操作,请点击下面的按钮重置密码:</p>
<div style="text-align: center;">
<a th:href="${resetUrl}" class="btn">重置密码</a>
</div>
<div class="warning">
<strong>安全提醒:</strong>
<ul>
<li>此链接仅在 <span th:text="${expireTime}">30分钟</span> 内有效</li>
<li>如果不是您的操作,请忽略此邮件</li>
<li>为保护账户安全,建议定期更换密码</li>
</ul>
</div>
<p>如果按钮无法点击,请复制以下链接:</p>
<p style="word-break: break-all; background: #f8f9fa; padding: 10px;">
<span th:text="${resetUrl}">重置链接</span>
</p>
</div>
</div>
</body>
</html>
控制器示例
@RestController
@RequestMapping("/api/email")
@Slf4j
public class EmailController {
@Autowired
private EmailService emailService;
@PostMapping("/send-verify")
public ResponseEntity<String> sendVerifyEmail(@RequestBody EmailRequest request) {
try {
Map<String, Object> variables = new HashMap<>();
variables.put("username", request.getUsername());
variables.put("verifyUrl", "https://yourdomain.com/verify?token=" + request.getToken());
variables.put("expireTime", "24小时");
emailService.sendTemplateEmail(
request.getEmail(),
"账户验证 - 请验证您的邮箱",
"register-verify",
variables
);
return ResponseEntity.ok("验证邮件发送成功");
} catch (Exception e) {
log.error("发送验证邮件失败", e);
return ResponseEntity.status(500).body("邮件发送失败");
}
}
@PostMapping("/send-reset")
public ResponseEntity<String> sendResetEmail(@RequestBody EmailRequest request) {
try {
Map<String, Object> variables = new HashMap<>();
variables.put("username", request.getUsername());
variables.put("resetUrl", "https://yourdomain.com/reset?token=" + request.getToken());
variables.put("expireTime", "30分钟");
emailService.sendTemplateEmail(
request.getEmail(),
"密码重置 - 重置您的账户密码",
"password-reset",
variables
);
return ResponseEntity.ok("重置邮件发送成功");
} catch (Exception e) {
log.error("发送重置邮件失败", e);
return ResponseEntity.status(500).body("邮件发送失败");
}
}
}
最佳实践
1. 异步发送邮件
为了提高用户体验,建议使用异步方式发送邮件:
@Service
public class AsyncEmailService {
@Autowired
private EmailService emailService;
@Async
public CompletableFuture<Void> sendEmailAsync(String to, String subject,
String templateName, Map<String, Object> variables) {
try {
emailService.sendTemplateEmail(to, subject, templateName, variables);
return CompletableFuture.completedFuture(null);
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
}
}
}
2. 邮件发送限流
@Component
public class EmailRateLimiter {
private final Map<String, Long> lastSendTime = new ConcurrentHashMap<>();
private final long RATE_LIMIT_INTERVAL = 60000; // 1分钟
public boolean canSend(String email) {
long currentTime = System.currentTimeMillis();
Long lastTime = lastSendTime.get(email);
if (lastTime == null || currentTime - lastTime > RATE_LIMIT_INTERVAL) {
lastSendTime.put(email, currentTime);
return true;
}
return false;
}
}
3. 邮件发送日志记录
@Entity
@Table(name = "email_log")
public class EmailLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String recipient;
private String subject;
private String template;
private String status; // SUCCESS, FAILED
private String errorMessage;
private LocalDateTime sendTime;
// getter/setter...
}
常见问题及解决方案
1. 邮件发送失败
问题: AuthenticationFailedException: 535 Login Fail
解决方案:
- 确认邮箱用户名和授权码正确
- 检查邮箱服务商是否开启SMTP服务
- 验证服务器网络是否可访问邮箱服务器
2. 中文乱码问题
解决方案:
helper.setText(htmlContent, true);
// 设置编码
message.setHeader("Content-Type", "text/html; charset=UTF-8");
3. 模板渲染失败
确保模板路径正确,变量名称与模板中的占位符一致。
总结
在Spring Boot项目中实现邮件模板功能。主要包括:
- 环境配置:添加必要依赖和邮箱配置
- 服务实现:创建邮件发送服务,支持简单邮件、模板邮件和带附件邮件
- 模板设计:使用Thymeleaf创建美观的HTML邮件模板
- 最佳实践:异步发送、限流控制、日志记录等
邮件功能是Web应用的重要组成部分,合理的邮件模板设计不仅能提升用户体验,还能有效传达信息。希望本文能帮助你快速掌握Spring Boot邮件功能的开发。
在实际项目中,还需要考虑邮件发送的稳定性、安全性和性能优化等方面。建议根据具体业务需求进行适当调整和扩展。



……
