解决spring boot JavaMailSender部分收件人错误导致发送失败的问题

  使用spring boot通常使用spring-boot-starter-mail进行邮件的发送。当进行邮件群发的话,如果一个收件人的地址错误,会导致所有邮件都发送失败。因此我们需要在邮件发送失败的时候把错误的收件人移除,重新发送。

  当邮件发送失败的时候会抛出MailSendException,异常信息中包含错误的收件人信息。

  主要代码如下:

private void sendMail(List<String> mailList, MimeMessageHelper message){
    try{
        this.mailSender.send(message.getMimeMessage());
    }catch (MailSendException e){
        Set<String> tmpInvalidMails = getInvalidAddress(e);
        // 非无效收件人导致的异常,暂不处理
        if (tmpInvalidMails.isEmpty()){
            logger.error(e.getMessage());
            return;
        }
        mailList.removeAll(tmpInvalidMails);
        if(mailList.isEmpty()){
            logger.error("邮件发送失败,无收件人" + e.getMessage());
            return;
        }
        message.setTo(mailList.toArray(new String[0]));
        sendMail(mailList, message)
    }
}

  捕获邮件发送失败的异常,首先判断是否是收件人无效导致的异常。从异常中解析无效收件人,收件人例表中移除无效的收件人,重新发送邮件。

  如何从从异常中获取无效收件人,首先看下JavaMailSenderImpl这个类的doSend方法

 1 protected void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException {
 2     Map<Object, Exception> failedMessages = new LinkedHashMap<>();
 3     Transport transport = null;
 4     try {
 5         for (int i = 0; i < mimeMessages.length; i++) {
 6             ...
 7             // Send message via current transport...
 8             MimeMessage mimeMessage = mimeMessages[i];
 9             try {
10                 ...
11             }
12             catch (Exception ex) {
13                 Object original = (originalMessages != null ? originalMessages[i] : mimeMessage);
14                 failedMessages.put(original, ex);
15             }
16         }
17     }
18     finally {
19         try {
20             if (transport != null) {
21                 transport.close();
22             }
23         }
24         catch (Exception ex) {
25             if (!failedMessages.isEmpty()) {
26                 throw new MailSendException("Failed to close server connection after message failures", ex,
27                         failedMessages);
28             }
29             else {
30                 throw new MailSendException("Failed to close server connection after message sending", ex);
31             }
32         }
33     }
34
35     if (!failedMessages.isEmpty()) {
36         throw new MailSendException(failedMessages);
37     }
38 }

  当邮件发送过程中遇到异常会保存到failedMessages中,我们需要从中解析收件人无效导致的异常。

  接着继续看源码SMTPTransport的rcptTo方法,会去校验每个收件人,通过向服务器发送RCPT TO:<地址>,根据响应码来判断收件人是否有效。

protected void rcptTo() throws MessagingException {
    List<InternetAddress> valid = new ArrayList();
    List<InternetAddress> validUnsent = new ArrayList();
    List<InternetAddress> invalid = new ArrayList();
    ...
    int k;
    for(k = 0; k < this.addresses.length; ++k) {
        sfex = null;
        InternetAddress ia = (InternetAddress)this.addresses[k];
        String cmd = "RCPT TO:" + this.normalizeAddress(ia.getAddress());
        if (dsn) {
            cmd = cmd + " NOTIFY=" + notify;
        }

        this.sendCommand(cmd);
        int retCode = this.readServerResponse();
        switch(retCode) {
        case 250:
        case 251:
            valid.add(ia);
            ...
            break;
        case 450:
        case 451:
        case 452:
        case 552:
            ...
            validUnsent.add(ia);
            ...
            break;
        case 501:
        case 503:
        case 550:
        case 551:
        case 553:
            ...
            invalid.add(ia);
            ...
            break;
        default:
            if (retCode >= 400 && retCode <= 499) {
                validUnsent.add(ia);
            } else {
                ...
                invalid.add(ia);
            }
            ...
        }
    }

    if (sendPartial && valid.size() == 0) {
        sendFailed = true;
    }

    int lrc;
    if (sendFailed) {
        this.invalidAddr = new Address[invalid.size()];
        invalid.toArray(this.invalidAddr);
        this.validUnsentAddr = new Address[valid.size() + validUnsent.size()];
        k = 0;

        for(lrc = 0; lrc < valid.size(); ++lrc) {
            this.validUnsentAddr[k++] = (Address)valid.get(lrc);
        }

        for(lrc = 0; lrc < validUnsent.size(); ++lrc) {
            this.validUnsentAddr[k++] = (Address)validUnsent.get(lrc);
        }
    }
    ...

    if (sendFailed) {
        this.logger.fine("Sending failed because of invalid destination addresses");
        ...
        throw new SendFailedException("Invalid Addresses", (Exception)mex, this.validSentAddr, this.validUnsentAddr, this.invalidAddr);
    }
}

  当收件人无效发送失败会抛出SendFailedException异常,异常中包含收件人是否有效的信息。

  因此我们只要从failedMessages查找是否含有SendFailedException,然后从SendFailedException直接得到无效的收件人信息。代码如下:

    private Set<String> getInvalidAddress(MailSendException e){
        Set<String> mails = new HashSet<>();
        for(Exception exception: e.getFailedMessages().values()){
            if(exception instanceof SendFailedException){
                for(Address address: ((SendFailedException) exception).getInvalidAddresses()){
                    mails.add(address.toString());
                }
            }
        }
        return mails;
    }

  

原文地址:https://www.cnblogs.com/lilinwei340/p/9886580.html

时间: 2024-08-01 18:10:47

解决spring boot JavaMailSender部分收件人错误导致发送失败的问题的相关文章

解决Spring Boot集成Shiro,配置类使用Autowired无法注入Bean问题

如题,最近使用spring boot集成shiro,在shiroFilter要使用数据库动态给URL赋权限的时候,发现 @Autowired 注入的bean都是null,无法注入mapper.搜了半天似乎网上都没有相关问题,也是奇怪.最后发现 /** * Shiro生命周期处理器 * * @return */ @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor getLifecycle

解决spring boot中文乱码问题

在开发或学习当中,我们不可避免的会碰到中文乱码的问题(好想哭,但还是要保持微笑!) 今天,在学习spring boot中碰到了中文乱码问题. 首先,看了一下workspace是不是设置utf-8默认字符集: 1)看了下没有,设置工作空间字符集为utf-8默认.(http://www.cnblogs.com/lhns/p/8901968.html) 2)发现没有效果. 3)在传值的地址上限制它的字符集格式: 这时候就显示正常了: 下次再看下有没有关于设置配置文件的修改,再给大家提供下思路.....

Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题

Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另一种是CGLib动态代理. JDK动态代理 JDK 1.3版本以后提供了动态代理,允许开发者在运行期创建接口的代理实例,而且只能为接口创建代理实例. 如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生成被代理接口的新的匿名实现类. JDK动态代理具体实现原理: 通过实现InvocationHandlet接口创建自己的调用处理器: 通过为Proxy类指定ClassLoader对象和一组in

解决spring boot jpa查询,语句正确,返回为空问题

JPA(Hibernate),拥有很强大的能力,极大简化DAO层的开发 Spring Boot实现的是一站式全包,但是数据库是比较特殊的存在,在开发时可以使用Embedded数据库如DerBy等,但是实际环境下,都会把数据库分离到另一台专用服务器,在连接的时候,可能会出现和开发时不一样的各种BUG 调用查询接口,结果如下: [ {}, {}, {} ] 在调用它的时候会返回null 原因有2 1.传进去的参数可能不正确 需要我们debug或输出,细细查看 2.查询字符串有中文 原因可能是字符集的

Spring Boot常见配置及错误

一.SpringBoot常见配置 (1)SpingBoot与MyBatis集成时跟踪SQL语句 log4j: logger: java: sql: ResultSet: TRACE (2)日志跟踪 debug: true logging: config: classpath:log4j2/log4j2.xml (3)MyBatis集成: #mybatis配置 mybatis: #配置映射类所在包名 type-aliases-package: com.xx.it.model (数据库实体对象所在路

TortoiseSVN-1.8.11 安装时弹出2503错误导致安装失败解决办法

这个问题主要是由于msi格式文件在win8中默认不是以管理员身份运行造成,可通过命令行解决: 右键单击win8左下角启动图标,选择命令提示符(管理员): 输入:msiexec /package 要安装msi程序完整路径及文件名,回车执行: 安装成功:

解决Spring Boot 使用RedisTemplate 存储键值出现乱码 \xac\xed\x00\x05t\x00

最近使用spring-data-redis RedisTemplate 操作redis时发现存储在redis中的key不是设置的string值,前面还多出了许多类似\xac\xed\x00\x05t\x00这种字符串,如下 [html] view plain copy print? 127.0.0.1:6379> keys * 1) "\xac\xed\x00\x05t\x00\x04pass" 2) "\xac\xed\x00\x05t\x00\x04name&quo

Spring boot 定制自己的错误

1).如何定制错误的页面: ? 1).有模板引擎的情况下:error/状态码; [将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下],发生此状态码的错误就会来到 对应的页面: ? 我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html): ? 页面能获取的信息: ? timestamp:时间戳 ? status:状态码 ? error:错误提示 ? exception:异常对象 ? message:异常

解决spring boot启动报错java.lang.NoClassDefFoundError: ch/qos/logback/classic/Level

学习了:https://blog.csdn.net/yunfeng482/article/details/78106433 没想到啊:我都看见了,idea也反编不了: 没想到要重新下载啊: 原文地址:https://www.cnblogs.com/stono/p/9126417.html