你有没有觉得邮件发送人固定配置在yml文件中是不妥当的呢?S

这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

明月当天,不知道你有没有思念的人

前言

之前其实已经写过SpringBoot异步发送邮件,但是今天在一个小项目中要用到发送邮件时,我突然觉得邮件发送人只有一个,并且固定写在yml文件中,就是非常的不妥当,就想着怎么整成一个动态的。

在写之前已经翻过很多博客了,该踩的坑都踩的差不多了,我是实现之后写的文章,有问题大家可以一起交流。

小声bb(对于CSDN我已经逐渐变得麻木了,真的简称CV大法现场)。

于是就有了下面这篇文章啦….

一、需求分析

默认大家都已经会 SpringBoot 集成 邮件发送啦哈,不行的,点一下上文的链接啦。

我先说说我想要达到什么样的效果:

  1. 邮件发送人可以是多个,yml文件中是兜底配置(即数据库中没有一个可用时,使用yml文件中配置的邮件发送人)
  2. 项目启动后,我也可以临时增加邮件发送人,或者禁用掉某个邮件发送人(操作完也无需重启项目即可生效)
  3. 发送邮件内容为html;另外异步发送邮件(可有可无,大家都会)

思路其实蛮简单的,就只要做到每次我们新添加或者修改邮件发送人配置的时候,对JavaSendMailImpl这个类重新初始化即可。这个地方没啥可讲的,就是不让框架给我们自动配置,我们手动来即可。

二、详细步骤

2.1、编码

1)yml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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安全协议的非对称加密的高度安全可靠性,可防止邮件泄露。SMTPSSMTP协议一样,也是用来发送邮件的,只是更安全些,防止邮件被黑客截取泄密,还可实现邮件发送者抗抵赖功能。防止发送者发送之后删除已发邮件,拒不承认发送过这样一份邮件。端口465和587便是基于SMTPS协议开放的。

465端口(SMTPS)︰它是SMTPS协议服务所使用的其中一个端口,它在邮件的传输过程中是加密传输(SSL/TLS)的,相比于SMTP协议攻击者无法获得邮件内容,邮件在一开始就被保护了起来。

所以实际上我们使用的配置应该是stmps


另外建个properties资源类 与 配置文件一一对应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
java复制代码/**
* @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文件,我们大致知道了要建立张什么样的数据表了哈。

image-20211127003820993

这些大家都可以自定义哈,根据自己需求来建哈。

根据数据表建一个pojo类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
java复制代码/**
* @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层

1
2
3
java复制代码@Repository
public interface MailMapper extends BaseMapper<MailPO> {
}

service

1
2
3
4
5
6
7
8
9
10
11
java复制代码/**
* @Author: crush
* @Date: 2021-11-26 15:55
* version 1.0
*/
public interface MailService {

void send(MailDTO mailDTO);

boolean addMailPerson(MailPO mailPO);
}

impl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
java复制代码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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码/**
* @author crush
* 邮箱发送-前端传输参数
*/
@Data
public class MailDTO implements Serializable {

/*** 接受邮箱账户*/
private String mail;
/*** 邮箱标题*/
private String title;
/** * 要发送的内容*/
private String content;
}

2.4、MailSenderConfig 配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
java复制代码/**
* @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注解。下次再一起研究。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码/**
* 初始化操作
* 目前只定义了动态设置邮件发送人的操作
* @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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java复制代码/**
* @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;
}

}

三、测试

image-20211127011047427

模板大致就是如下状态吧。 具体样式大家再自己调吧 😁

image-20211127011111724

是添加进去的

image-20211127011530374

多点了一次哈。

image-20211127011522418

我再点击发送邮件,因为是随机数的方式,我们多测试几次,总会用到这个错误的邮件发送人的,用到了就表示我们已经成功啦哈。

因为添加的随便输入的,肯定是失败的哈。但是可以确定我们用到了我们项目启动后加入的邮件发送人啦。 你们可以填入争取的试一试。

image-20211127011707568

结束了结束啦。

没写小demo,没啥源码。

后语

大家一起加油!!!如若文章中有不足之处,请大家及时指出,在此郑重感谢。

纸上得来终觉浅,绝知此事要躬行。

大家好,我是博主宁在春主页

一名喜欢文艺却踏上编程这条道路的小青年。

希望:我们,待别日相见时,都已有所成


难得回到后端肝篇文,又拾起后端了,之后还会接着写Vue的,肯定会把专栏写完的。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%