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

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


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

前言

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

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

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

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

一、需求分析

默认大家都已经会 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文件,我们大致知道了要建立张什么样的数据表了哈。

1704463405866.jpg

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

根据数据表建一个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;
    }
}

三、测试

1704463424026.jpg

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

1704463426226.jpg

是添加进去的

1704463428740.jpg

多点了一次哈。

1704463453614.jpg

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

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

1704463455377.jpg

结束了结束啦。

没写小demo,没啥源码。

后语

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

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

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

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

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


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

目录
相关文章
|
2月前
|
Java Spring
Spring Boot配置的优先级?
在Spring Boot项目中,配置可通过配置文件和外部配置实现。支持的配置文件包括application.properties、application.yml和application.yaml,优先级依次降低。外部配置常用方式有Java系统属性(如-Dserver.port=9001)和命令行参数(如--server.port=10010),其中命令行参数优先级高于系统属性。整体优先级顺序为:命令行参数 &gt; Java系统属性 &gt; application.properties &gt; application.yml &gt; application.yaml。
506 0
|
6月前
|
XML Java Maven
springboot-多环境配置文件
本文介绍了如何创建开发和生产环境的配置文件,并在IDEA和Maven中进行配置。开发环境中,通过设置profile为`dev`来指定配置文件;生产环境中,使用Maven命令参数`-Pprod`打包并指定配置文件。公共配置可放在`application.yml`中统一管理。日志配置需确保`logback-spring.xml`中的profile正确,以保证日志正常输出。
326 4
springboot-多环境配置文件
|
1月前
|
安全 算法 Java
在Spring Boot中应用Jasypt以加密配置信息。
通过以上步骤,可以在Spring Boot应用中有效地利用Jasypt对配置信息进行加密,这样即使配置文件被泄露,其中的敏感信息也不会直接暴露给攻击者。这是一种在不牺牲操作复杂度的情况下提升应用安全性的简便方法。
582 10
|
6月前
|
缓存 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
691 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
6月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
874 0
|
6月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
89 0
|
6月前
|
SQL Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— application.yml 中对日志的配置
在 Spring Boot 项目中,`application.yml` 文件用于配置日志。通过 `logging.config` 指定日志配置文件(如 `logback.xml`),实现日志详细设置。`logging.level` 可定义包的日志输出级别,例如将 `com.itcodai.course03.dao` 包设为 `trace` 级别,便于开发时查看 SQL 操作。日志级别从高到低为 ERROR、WARN、INFO、DEBUG,生产环境建议调整为较高级别以减少日志量。本课程采用 yml 格式,因其层次清晰,但需注意格式要求。
587 0
|
2月前
|
人工智能 安全 Java
Spring Boot yml 配置敏感信息加密
本文介绍了如何在 Spring Boot 项目中使用 Jasypt 实现配置文件加密,包含添加依赖、配置密钥、生成加密值、在配置中使用加密值及验证步骤,并提供了注意事项,确保敏感信息的安全管理。
713 1
|
7月前
|
存储 前端开发 Java
Springboot静态资源映射及文件映射
在Spring Boot项目中,为了解决前端访问后端存储的图片问题,起初尝试通过静态资源映射实现,但发现这种方式仅能访问打包时已存在的文件。对于动态上传的图片(如头像),需采用资源映射配置,将特定路径映射到服务器上的文件夹,确保新上传的图片能即时访问。例如,通过`addResourceHandler(&quot;/img/**&quot;).addResourceLocations(&quot;file:E:\\myProject\\forum_server\\&quot;)`配置,使前端可通过URL直接访问图片。
402 0
Springboot静态资源映射及文件映射
|
6月前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现

热门文章

最新文章