• 解决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 }
    View Code

      当邮件发送过程中遇到异常会保存到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);
        }
    }
    View Code

      当收件人无效发送失败会抛出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;
        }
    

      

  • 相关阅读:
    树状数组
    #135. 二维树状数组 3:区间修改,区间查询
    poj 2155 (二维树状数组 区间修改 求某点值)
    #133. 二维树状数组 1:单点修改,区间查询
    poj 3468 (区间修改 区间查询)
    树状数组 模板
    1535:【例 1】数列操作
    最通俗全面理解application context中的context是什么意思
    牛客哈理工小乐乐下象棋(深度理解bfs好题)
    牛客哈理工小乐乐打游戏(bfs深度理解好题)
  • 原文地址:https://www.cnblogs.com/lilinwei340/p/9886580.html
Copyright © 2020-2023  润新知