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

27次阅读

共计 8592 个字符,预计需要花费 22 分钟才能阅读完成。

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

前言

之前其实曾经写过 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 的,必定会把专栏写完的。

正文完
 0