乐趣区

关于java:你有没有觉得邮件发送人固定配置在yml文件中是不妥当的呢SpringBoot-动态设置邮件发送人

明月当天,不晓得你有没有怀念的人

前言

之前其实曾经写过 SpringBoot 异步发送邮件,然而明天在一个小我的项目中要用到发送邮件时,我忽然感觉邮件发送人只有一个,并且固定写在 yml 文件中,就是十分的不得当,就想着怎么整成一个动静的。

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

小声 bb(对于 CSDN 我真的逐步变得麻痹了,简称 CV 大法现场,尽管我自己也是 CSDN 的一名小小博主,也是资深用户,对于文章的这块很多时候真的没法说,除了能说加油也没有了吧)。

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


一、需要剖析

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

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

  1. 邮件发送人能够是多个,yml 文件中是兜底配置(即数据库中没有一个可用时,应用 yml 文件中配置的邮件发送人)
  2. 我的项目启动后,我也能够长期减少邮件发送人,或者禁用掉某个邮件发送人(操作完也无需重启我的项目即可失效)
  3. 发送邮件内容为 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 平安协定的非对称加密的高度平安可靠性,可避免邮件泄露。SMTPSSMTP 协定一样,也是用来发送邮件的,只是更平安些,避免邮件被黑客截取泄密,还可实现邮件发送者抗抵赖性能。避免发送者发送之后删除已发邮件,拒不承认发送过这样一份邮件。端口 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 的,必定会把专栏写完的。

退出移动版