作者:ChenZhen
博客地址:https://www.chenzhen.space/
版权:本文为博主 ChenZhen 的原创文章,本文版权归作者所有,转载请附上原文出处链接及本声明。
如果对你有帮助,请给一个小小的star⭐
邮件提醒功能:当你收到某个人的回复时,会给你发送一封提醒邮件,并展示回复的内容。
我觉得对于一个博客,邮件回复的功能是必不可少的,能让你及时的回复别人的评论,还能让我更方便的和网上的人对线。
其实这个功能还是蛮好实现的,我们先演示怎么用java发送一封简单的邮件
1.开启POP3/SMTP服务
以QQ邮箱为例:
进入设置
在下方开启POP3/SMTP服务,此处已开启
经过身份认证过后,会获得一串授权码,请务必保存下来
2.引入spring-boot-starter-mail 依赖
由于Spring推出了关于Mail的JavaMailSender类,基于该类Spring Boot又对其进行了进一步封装,从而实现了轻松发送邮件的集成。而且JavaMailSender类提供了强大的邮件发送能力,支持各种类型的邮件发送。
<!-- 邮件发送功能 启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
3.yaml文件配置
spring:
mail:
host: smtp.qq.com # 配置 smtp 服务器地址
port: 25 # smtp 服务器端口
username: xxxxx #配置你的邮箱地址
password: xxxxx #配置申请到的授权码
protocol: smtp #协议
thymeleaf-html: mail #设置要解析发送的html模板
4.测试发送普通邮件
配置完后,我们来测试发送一封普通的邮件
- 在这里有两个关键的对象,JavaMailSender负责发送邮件,SimpleMailMessage负责构建邮件内容对象,我们使用这两个对象来发送邮件。
由于Spring Boot的starter模块提供了自动化配置,在引入了spring-boot-starter-mail依赖之后,会根据配置文件中的内容去创建JavaMailSender实例并交给spring管理,因此我们可以直接在需要使用的地方直接@Autowired来引入 JavaMailSender 邮件发送对象
注意:使用@Autowired注解的类必须交由spring管理,即加上@Component注解
使用SimpleMailMessage对象来构建一封邮件
@Test
public void test5(){
//构建邮件内容对象
SimpleMailMessage msg = new SimpleMailMessage();
//邮件发送者
msg.setFrom("1583296383@qq.com");
//邮件接收者
msg.setTo("1583296383@qq.com");
//邮件主题
msg.setSubject("测试邮件主题");
//邮件正文
msg.setText("测试邮件内容");
//邮件发送时间
msg.setSentDate(new Date());
//邮件发送
javaMailSender.send(msg);
}
运行测试方法,需要等待一会便能收到
5.发送静态邮件模板
上面演示了如何发送普通邮件,接下来我们要实现发送一封静态邮件模板,就像开头的实例一样
这里我们使用Thymeleaf作为模板
引入Thymeleaf启动器
<!--Thymeleaf 静态模板jar包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
自定义静态模板
这里可以自己自定义好看的邮件模板,这里我就简单的测试一下
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"><head>
<meta charset="UTF-8">
<title>Thymeleaf邮件模板</title>
</head>
<body>
<p>这是一封Thymeleaf邮件模板</p>
<table border="1">
<tr>
<td>姓名</td>
<td th:text="${name}"></td>
</tr>
<tr>
<td>年龄</td>
<td th:text="${age}"></td>
</tr>
</table>
<div style="color: red;">这是一封Thymeleaf邮件模板</div>
</body>
</html>
将该html文件放到templates目录下,这是springboot放置模板文件的默认路径
运行测试方法
- 这里使用MimeMessageHelper来构建邮件,利用Context对象可以设置模板里面的Thymeleaf表达式的值
- 调用springTemplateEngine的process方法来解析模板,第一个参数为模板文件的名字
@Test
public void test4() throws MessagingException {
MimeMessage msg = javaMailSender.createMimeMessage();//构建邮件
MimeMessageHelper helper = new MimeMessageHelper(msg, true);//设置可选文本或添加内联元素或附件,
helper.setFrom("1583296383@qq.com");//发件人
helper.setSentDate(new Date());//发送日期
helper.setSubject("这是测试主题(thymeleaf)");//发送主题
helper.setTo("1583296383@qq.com");//收件人
Context context = new Context();//构建上下文环境
context.setVariable("name","高级工程师");
context.setVariable("age", 19);
String process = springTemplateEngine.process("table", context);//将模板解析成静态字符串
helper.setText(process,true);//内容是否设置成html,true代表是
javaMailSender.send(msg);//发送
}
成功收到
- 现在我们将一些操作进行封装,做成一个工具类,并且新建一个Mail对象来简化发送的过程,完成博客的邮件提醒功能。
Mail类
package com.chenzhen.blog.pojo;
import java.util.Date;
/**
* @author ChenZhen
* @Description
* @create 2022/9/27 11:52
* @QQ 1583296383
* @WeXin(WeChat) ShockChen7
*/
public class Mail {
//发件人邮箱账号(固定为我自己 即博主本人)
private String sendMailAccount;
//收件人邮箱账号
private String acceptMailAccount;
//收件人姓名
private String name;
//收件人评论的内容
private String comment;
//回复收件人的 回复者的姓名
private String respondent;
//回复者的回复内容
private String reply;
//评论发生的地方链接(回复者是在哪里回复收件人的)
private String address;
//邮件主题
private String theme;
//发送时间
private Date sendTime = new Date();
public Mail() {
}
public Mail(String sendMailAccount, String acceptMailAccount, String name, String comment, String respondent, String reply, String address, String theme) {
this.sendMailAccount = sendMailAccount;
this.acceptMailAccount = acceptMailAccount;
this.name = name;
this.comment = comment;
this.respondent = respondent;
this.reply = reply;
this.address = address;
this.theme = theme;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSendMailAccount() {
return sendMailAccount;
}
public void setSendMailAccount(String sendMailAccount) {
this.sendMailAccount = sendMailAccount;
}
public String getAcceptMailAccount() {
return acceptMailAccount;
}
public void setAcceptMailAccount(String acceptMailAccount) {
this.acceptMailAccount = acceptMailAccount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getRespondent() {
return respondent;
}
public void setRespondent(String respondent) {
this.respondent = respondent;
}
public String getReply() {
return reply;
}
public void setReply(String reply) {
this.reply = reply;
}
public String getTheme() {
return theme;
}
public void setTheme(String theme) {
this.theme = theme;
}
public Date getSendTime() {
return sendTime;
}
public void setSendTime(Date sendTime) {
this.sendTime = sendTime;
}
}
邮件工具类MailUtil
- 有一个地方需要额外注意,发送提醒邮件的功能是在用户进行评论后的操作,但是邮件的发送过程需要花一段时间,会造成用户点了评论发送按钮之后很久才得到反馈,为了解决这个问题,我们需要运用到多线程思想。
- 我们将发送邮件的方法sendThymeleafEmail()加上@Async注解,将该方法标记为一个异步方法,这样在执行该方法的时候springboot会为我们开辟一个另外的线程来运行邮件的发送功能。这样不会造成线程的堵塞
package com.chenzhen.blog.util;
import com.chenzhen.blog.pojo.Mail;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
/**
* @author ChenZhen
* @Description
* @create 2022/9/27 11:59
* @QQ 1583296383
* @WeXin(WeChat) ShockChen7
*/
@Component
public class MailUtil {
@Autowired
private JavaMailSender javaMailSender;//引入 JavaMailSender 邮件发送对象 来实现发送邮件的功能
@Autowired
private SpringTemplateEngine springTemplateEngine;//Spring 模板引擎
@Value("${spring.mail.username}") //从yaml配置文件中获取
private String from; //发送方邮箱地址
@Value("${spring.mail.thymeleaf-html}")//从yaml配置文件中获取
private String html;
/**
* 发送 thymeleaf 页面邮件
*/
@Async("asyncThreadPoolTaskExecutor") //设置为一个异步方法
public void sendThymeleafEmail(Mail mail) throws MessagingException {
MimeMessage msg = javaMailSender.createMimeMessage();//构建邮件
MimeMessageHelper helper = new MimeMessageHelper(msg, true);//构建邮件收发信息。
helper.setFrom(from);//发件人(默认固定为自己)
helper.setSentDate(mail.getSendTime());//发送日期
helper.setSubject(mail.getTheme());//发送主题
Context context = new Context();//将mail中的值设置进context交由模板引擎渲染
// WebContext context = new WebContext(request, response, request.getServletContext(), request.getLocale());
context.setVariable("name",mail.getName());
context.setVariable("theme", mail.getTheme());
context.setVariable("comment", mail.getComment());
context.setVariable("respondent", mail.getRespondent());
context.setVariable("reply", mail.getReply());
context.setVariable("address", mail.getAddress());
String process = springTemplateEngine.process(html, context);
helper.setText(process,true);//内容是否设置成html,true代表是
helper.setTo(mail.getAcceptMailAccount());//收件人
javaMailSender.send(msg);//发送
}
}
自定义线程池配置类
- @Async后的属性参数值是指定使用哪个线程池,这里我们自己配置一个线程池来让Async指定
package com.chenzhen.blog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author ChenZhen
* @Description 自定义线程池
* @create 2022/9/28 18:31
* @QQ 1583296383
* @WeXin(WeChat) ShockChen7
*/
@Configuration
public class AsyncPoolConfig {
@Bean(name = "asyncThreadPoolTaskExecutor")
public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(200);
executor.setQueueCapacity(25);
executor.setKeepAliveSeconds(200);
executor.setThreadNamePrefix("asyncThread");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
在Service层添加发送邮件功能
对用户进行判断:
- 如果是游客评论则会通知管理员,如果是管理员自己发的评论则不需要通知自己
- 如果是回复别人的评论,则都进行邮件通知
public void sendMail(User user, Message message) throws MessagingException {
if (user!=null){
//如果是管理员发的评论
if (message.getParentMessage().getId()==null || message.getParentMessage()==null){
//如果是根评论
//不需要发给自己邮件
return;
}else {
//如果不是根评论,则给[我回复的对象]发一封提醒邮件
Message parentMessage = messageMapper.selectById(message.getParentMessage().getId());//获取父评论
Mail mail = new Mail(null, parentMessage.getEmail(), parentMessage.getNickname(), parentMessage.getContent(),
message.getNickname(), message.getContent(),
"/message", "您在《ChenZhen的客栈-留言板》中的评论有了新的回复!");
mailUtil.sendThymeleafEmail(mail);
}
}else {
//如果不是管理员发的评论
if (message.getParentMessage().getId()==null || message.getParentMessage()==null){
//如果是根评论
//发给我自己,提醒有人在留言板留言了
Mail mail = new Mail(null, "1583296383@qq.com", "ChenZhen", null,
message.getNickname(), message.getContent(),
"/message","在《ChenZhen的客栈-留言板》中有了新的留言!");
mailUtil.sendThymeleafEmail(mail);
}else{
//如果不是根评论
//给回复者[回复的对象]发一份提醒邮件
Message parentMessage = messageMapper.selectById(message.getParentMessage().getId());//获取父评论
Mail mail = new Mail(null,parentMessage.getEmail(),parentMessage.getNickname(),
parentMessage.getContent(),message.getNickname(),message.getContent(),
"/message","您在《ChenZhen的客栈-留言板》中的评论有了新的回复!");
mailUtil.sendThymeleafEmail(mail);
}
}
}
在保持到数据库后,调用Service层的方法,异步地给用户发送提醒邮件
- 自此邮件提醒功能就完成了!
评论