javascript
你有没有觉得邮件发送人固定配置在yml文件中是不妥当的呢?SpringBoot 动态设置邮件发送人
明月當天,不知道你有沒有思念的人
前言
之前其實已經寫過SpringBoot異步發送郵件,但是今天在一個小項目中要用到發送郵件時,我突然覺得郵件發送人只有一個,并且固定寫在yml文件中,就是非常的不妥當,就想著怎么整成一個動態的。
在寫之前已經翻過很多博客了,該踩的坑都踩的差不多了,我是實現之后寫的文章,有問題大家可以一起交流。
小聲bb(對于CSDN我真的逐漸變得麻木了,簡稱CV大法現場,雖然我本人也是CSDN的一名小小博主,也是資深用戶,對于文章的這塊很多時候真的沒法說,除了能說加油也沒有了吧)。
于是就有了下面這篇文章啦…
一、需求分析
默認大家都已經會 SpringBoot 集成 郵件發送啦哈,不行的,點一下上文的鏈接啦。
我先說說我想要達到什么樣的效果:
思路其實蠻簡單的,就只要做到每次我們新添加或者修改郵件發送人配置的時候,對JavaSendMailImpl這個類重新初始化即可。這個地方沒啥可講的,就是不讓框架給我們自動配置,我們手動來即可。
二、詳細步驟
2.1、編碼
1)yml配置文件
spring: mail:host: smtp.163.comusername: nxxxxxx@163.compassword: IXXXXXXXXXN(開啟允許第三方登錄后的授權碼)default-encoding: utf-8protocol: smtpsproperties:mail:smtp:port: 465auth: truestarttls:enable: truerequired: 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 {@AutowiredMailSenderConfig senderConfig;@AutowiredMailProperties mailProperties;@AutowiredMailMapper mailMapper;// 這里之前配置了一個線程池,上文的鏈接中有,就不說了哈// @Async("taskExecutor")@Overridepublic 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();}}// 添加就清空初始化的信息,重新初始化一遍即可。@Overridepublic 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 后來就沒去測試了。* 理論添加、修改完 調用這個初始化方法就可以了。*/ // @PostConstructpublic 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();}// 隨機返回一個JavaMailSenderreturn 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@Overridepublic 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 {@Autowiredprivate 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的,肯定會把專欄寫完的。
總結
以上是生活随笔為你收集整理的你有没有觉得邮件发送人固定配置在yml文件中是不妥当的呢?SpringBoot 动态设置邮件发送人的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 「后端小伙伴来学前端了」Vue中学会使用
- 下一篇: 「后端小伙伴来学前端了」Vue-Rout