明月当天,不晓得你有没有怀念的人
前言
之前其实曾经写过 SpringBoot 异步发送邮件,然而明天在一个小我的项目中要用到发送邮件时,我忽然感觉邮件发送人只有一个,并且固定写在 yml 文件中,就是十分的不得当,就想着怎么整成一个动静的。
在写之前曾经翻过很多博客了,该踩的坑都踩的差不多了,我是实现之后写的文章,有问题大家能够一起交换。
小声 bb(对于 CSDN 我真的逐步变得麻痹了,简称 CV 大法现场,尽管我自己也是 CSDN 的一名小小博主,也是资深用户,对于文章的这块很多时候真的没法说,除了能说加油也没有了吧)。
于是就有了上面这篇文章啦 ….
一、需要剖析
默认大家都曾经会 SpringBoot 集成 邮件发送啦哈,不行的,点一下上文的链接啦。
我先说说我想要达到什么样的成果:
- 邮件发送人能够是多个,yml 文件中是兜底配置(即数据库中没有一个可用时,应用 yml 文件中配置的邮件发送人)
- 我的项目启动后,我也能够长期减少邮件发送人,或者禁用掉某个邮件发送人(操作完也无需重启我的项目即可失效)
- 发送邮件内容为 html;另外异步发送邮件(可有可无,大家都会)
思路其实蛮简略的,就只有做到每次咱们新增加或者批改邮件发送人配置的时候,对 JavaSendMailImpl
这个类从新初始化即可。这个中央没啥可讲的,就是不让框架给咱们主动配置,咱们手动来即可。
二、具体步骤
2.1、编码
1)yml 配置文件
spring:
mail:
host: smtp.163.com
username: nxxxxxx@163.com
password: IXXXXXXXXXN(开启容许第三方登录后的受权码)
default-encoding: utf-8
protocol: smtps
properties:
mail:
smtp:
port: 465
auth: true
starttls:
enable: true
required: true
留神
: 对于邮件的协定protocol:smtps
的配置,我最开始也是配置的smtp
,我过后报的谬误是一个no provider for smtp 谬误
,我之前也写过始终用的是这个smtp 协定
,然而报了这个谬误,我就去搜寻,而后找到有篇博客说,
SMTPS 协定
SMTPS
(SMTP-over-SSL)是SMTP
协定基于SSL
平安协定之上的一种变种协定,它继承了SSL
平安协定的非对称加密的高度平安可靠性,可避免邮件泄露。SMTPS
和SMTP
协定一样,也是用来发送邮件的,只是更平安些,避免邮件被黑客截取泄密,还可实现邮件发送者抗抵赖性能。避免发送者发送之后删除已发邮件,拒不承认发送过这样一份邮件。端口 465 和 587 便是基于SMTPS
协定凋谢的。465 端口
(SMTPS)
︰它是SMTPS
协定服务所应用的其中一个端口,它在邮件的传输过程中是加密传输(SSL/TLS)
的,相比于SMTP
协定攻击者无奈取得邮件内容,邮件在一开始就被爱护了起来。
所以实际上咱们应用的配置应该是stmps
。
另外建个 properties
资源类 与 配置文件一一对应
/**
* @author crush
*/
@Data
@Component
@ConfigurationProperties(prefix = "spring.mail")
public class MailProperties {
/** * 用户名 */
private String username;
/** * 受权码 */
private String password;
/** * host */
private String host;
/** * 端口 */
private Integer port;
/*** 协定 */
private String protocol;
/** * 默认编码 */
private String defaultEncoding;
}
2.2、建表
依据 yml 文件,咱们大抵晓得了要建设张什么样的数据表了哈。
这些大家都能够自定义哈,依据本人需要来建哈。
依据数据表建一个 pojo 类。
/**
* @Author: crush
* @Date: 2021-11-26 18:28
* version 1.0
*/
@Data
@Accessors(chain = true)
@TableName("tb_email")
public class MailPO {
private String emailHost;
private String emailUsername;
private String emailPassword;
private Integer emailPort=465;
/** * 协定 */
private String protocol="smtps";
/** * 默认编码 */
private String defaultEncoding="utf-8";
/**
* 应用状态,1: 正在应用,2: 禁用,3: 停用
* TODO 前期应该更改为 枚举类来进行实现
*/
private Integer state=1;
/** * 创立工夫 */
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/*** 批改工夫 */
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
如果不是用 mybatis-plus 能够把创立工夫和批改工夫去掉 @TableField(fill = FieldFill.INSERT)
是 Mybatis-plus 中的注解。另外我主键是设置了自增,所以就空了。至于返回的类我用的 vo 包下的。
2.3、mapper、service 层
@Repository
public interface MailMapper extends BaseMapper<MailPO> {}
service
/**
* @Author: crush
* @Date: 2021-11-26 15:55
* version 1.0
*/
public interface MailService {void send(MailDTO mailDTO);
boolean addMailPerson(MailPO mailPO);
}
impl
import cn.hutool.core.util.IdUtil;
/**
* @author crush
* 邮箱发送实现类
*/
@Service
public class MailServiceImpl implements MailService {
@Autowired
MailSenderConfig senderConfig;
@Autowired
MailProperties mailProperties;
@Autowired
MailMapper mailMapper;
// 这里之前配置了一个线程池,上文的链接中有,就不说了哈
// @Async("taskExecutor")
@Override
public void send(MailDTO mailDTO) {
String context = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"\n" +
"<head>\n" +
"<meta charset=\"UTF-8\"/>\n" +
"<meta name=\"viewport\"content=\"width=device-width, initial-scale=1.0\"/>\n" +
"<title>xxxx 邮件 </title>\n" +
"<style>\n" +
"body {\n" +
"margin: 0;\n" +
"padding: 0;\n" +
"}\n" +
"\n" +
".email {\n" +
"position: relative;\n" +
"width: 100%;\n" +
"/* background-color: rgba(0, 0, 0, 1); */\n" +
"}\n" +
"\n" +
".main {\n" +
"left: 0;\n" +
"right: 0;\n" +
"margin: auto;\n" +
"width: 80%;\n" +
"max-width: 800px;\n" +
"box-sizing: content-box;\n" +
"}\n" +
"\n" +
".main .title {\n" +
"/* color: white; */\n" +
"display: inline-flex;\n" +
"align-items: center;\n" +
"}\n" +
"\n" +
".main .title span {\n" +
"margin: 0 10px;\n" +
"}\n" +
"\n" +
".main table {\n" +
"width: 100%;\n" +
"}\n" +
"\n" +
".main table tbody td {\n" +
"/* background-color: white; */\n" +
"padding: 20px;\n" +
"text-align: left;\n" +
"border-bottom: 1px solid rgb(161, 161, 161);\n" +
"}\n" +
"\n" +
"tfoot td p {\n" +
"color: rgb(161, 161, 161);\n" +
"font-size: 13px;\n" +
"}\n" +
"\n" +
"a {\n" +
"color: rgb(161, 161, 161);\n" +
"text-decoration: none;\n" +
"}\n" +
"\n" +
"a:hover {\n" +
"border-bottom: 1px solid rgb(161, 161, 161);\n" +
"}\n" +
"</style>\n" +
"</head>\n" +
"\n" +
"<body>\n" +
"<div class=\"email\">\n" +
"<div class=\"main\">\n" +
"<table>\n" +
"<thead>\n" +
"<tr>\n" +
"<td>\n" +
"<h1 class=\"title\">\n" +
"<img width=\"60\"src=\"xxxxx\"alt=\"\"/>\n" +
"<span>" + mailDTO.getTitle() + "</span>\n" +
"</h1>\n" +
"</td>\n" +
"</tr>\n" +
"</thead>\n" +
"<tbody>\n" +
"<tr>\n" +
"<td>\n" +
"" + mailDTO.getContent() +"\n"+" </td>\n"+" </tr>\n"+" </tbody>\n"+" <tfoot>\n"+" <tr>\n"+" <td>\n"+" <p> 邮件由零碎主动发送,请勿间接回复。</p>\n"+" <p> 官方网站:\n"+" <a href=\"https://blog.csdn.net/weixin_45821811?spm=1000.2115.3001.5343\"> 宁在春博客 </a>\n"+" </p>\n"+" </td>\n"+" </tr>\n"+" </tfoot>\n"+" </table>\n"+" </div>\n"+" </div>\n"+"</body>\n"+"\n"+"</html>";
JavaMailSenderImpl mailSender = senderConfig.getSender();
// 创立一个 SimpleMailMessage 对象
MimeMessage mimeMessage = mailSender.createMimeMessage();
// 须要创立一个 MimeMessageHelper 对象,相干参数和简略邮件相似
try {MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
// 发件人
helper.setFrom(mailSender.getUsername());
// 收件人 这个收件人能够是数组的,只是我这只须要单个 就没多做了。helper.setTo(mailDTO.getMail());
helper.setSubject("验证码");
// 将邮件内容设置为 html 格局
// 发送
helper.setText(context, true);
mailSender.send(mimeMessage);
} catch (MessagingException e) {e.printStackTrace();
}
}
// 增加就清空初始化的信息,从新初始化一遍即可。@Override
public boolean addMailPerson(MailPO mailPO) {if(mailMapper.insert(mailPO)>0){senderConfig.clear();
senderConfig.buildMailSender();
return true;
}
return false;
}
}
用到的MailDto
/**
* @author crush
* 邮箱发送 - 前端传输参数
*/
@Data
public class MailDTO implements Serializable {
/*** 承受邮箱账户 */
private String mail;
/*** 邮箱题目 */
private String title;
/** * 要发送的内容 */
private String content;
}
2.4、MailSenderConfig 配置类
/**
* @author crush
*/
@Slf4j
@Component
@AllArgsConstructor
public class MailSenderConfig {
private final List<JavaMailSenderImpl> senderList;
private final MailProperties mailProperties;
private final MailMapper mailMapper;
/**
* 初始化 sender
* PostConstruct 注解用于须要在依赖注入实现后执行任何初始化的办法。必须在类投入使用之前调用此办法
* 因为刚开始我感觉这种形式(@PostConstruct)不适合,就是没能做到批改了马上就能用的那种感觉。* 然而起初写完才发现,其实只有每次增加新的邮件发送人时,都从新初始化一次就能够了。* 起初我又用启动事件监听器。@PostConstruct 起初就没去测试了。* 实践增加、批改完 调用这个初始化办法就能够了。*/
// @PostConstruct
public void buildMailSender() {log.info("初始化 mailSender");
List<MailPO> mails = mailMapper.selectList(new QueryWrapper<MailPO>().eq("state", 1));
/**
* 需要:本来就是打算做成一个动静的邮件发送人,因为如果总是用一个邮件发送验证码或者是那种打搅短信,速度一旦太过于频繁,就会造成邮件发送谬误。* 思路:从数据库中拿到所有可用的邮件发送人,而后封装起来,之后发送邮件时,再进行随机的抉择即可。* 另外一种形式就是这是动静的。* 最初就是加个兜底的,如果数据库中查问不到邮件发送人,咱们应用配置文件中的发送邮件的配置。*/
if(mails!=null&&!mails.isEmpty()){
mails.forEach(mail -> {JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setDefaultEncoding(mail.getDefaultEncoding());
javaMailSender.setHost(mail.getEmailHost());
javaMailSender.setPort(mail.getEmailPort());
javaMailSender.setProtocol(mail.getProtocol());
javaMailSender.setUsername(mail.getEmailUsername());
javaMailSender.setPassword(mail.getEmailPassword());
// 增加数据
senderList.add(javaMailSender);
});
}
else{JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setDefaultEncoding(mailProperties.getDefaultEncoding());
javaMailSender.setHost(mailProperties.getHost());
javaMailSender.setPort(mailProperties.getPort());
javaMailSender.setProtocol(mailProperties.getProtocol());
javaMailSender.setUsername(mailProperties.getUsername());
javaMailSender.setPassword(mailProperties.getPassword());
// 增加数据
senderList.add(javaMailSender);
}
}
/**
* 获取 MailSender
*
* @return CustomMailSender
*/
public JavaMailSenderImpl getSender() {if (senderList.isEmpty()) {buildMailSender();
}
// 随机返回一个 JavaMailSender
return senderList.get(new Random().nextInt(senderList.size()));
}
/**
* 清理 sender
*/
public void clear() {senderList.clear();
}
}
2.5、监听器
一两句没啥说的,能够间接通过 idea 进去看源码上的 doc 注解。下次再一起钻研。
/**
* 初始化操作
* 目前只定义了动静设置邮件发送人的操作
* @Author: crush
* @Date: 2021-11-26 19:51
* version 1.0
*/
@Slf4j
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class StartListener implements ApplicationListener<ApplicationStartedEvent> {
MailSenderConfig mailSenderConfig;
public StartListener(MailSenderConfig mailSenderConfig) {this.mailSenderConfig = mailSenderConfig;}
@SneakyThrows
@Override
public void onApplicationEvent(@NotNull ApplicationStartedEvent event) {this.mailSenderConfig.buildMailSender();
}
}
2.6、controller
/**
* @Author: crush
* @Date: 2021-11-26 16:10
* version 1.0
*/
@RestController
@RequestMapping("/email")
public class MailController {
@Autowired
private MailService mailService;
@PostMapping("/send")
public String send(@RequestBody MailDTO mailDTO){mailService.send(mailDTO);
return "发送胜利!!!可能会稍有提早,请查看邮箱信息!!";
}
@PostMapping("/addConfig")
public String addMailPerson(@RequestBody MailPO mailPO){String message=mailService.addMailPerson(mailPO)?"增加胜利!!!不过,请留神:可能会有提早":"增加失败,请稍后重试!!";
return message;
}
}
三、测试
模板大抵就是如下状态吧。
是增加进去的
多点了一次哈。
我再点击发送邮件,因为是随机数的形式,咱们多测试几次,总会用到这个谬误的邮件发送人的,用到了就示意咱们曾经胜利啦哈。
因为增加的轻易输出的,必定是失败的哈。然而能够确定咱们用到了咱们我的项目启动后退出的邮件发送人啦。你们能够填入争取的试一试。
完结了完结啦。
没写小 demo,没啥源码。
后语
大家一起加油!!!如若文章中有不足之处,请大家及时指出,在此郑重感激。
纸上得来终觉浅,绝知此事要躬行。
大家好,我是博主
宁在春
:主页一名喜爱文艺却踏上编程这条路线的小青年。
心愿:
咱们,待别日相见时,都已有所成
。
难得回到后端肝篇文,又拾起后端了,之后还会接着写 Vue 的,必定会把专栏写完的。