使用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方法

 protected void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException {
Map<Object, Exception> failedMessages = new LinkedHashMap<>();
Transport transport = null;
try {
for (int i = 0; i < mimeMessages.length; i++) {
...
// Send message via current transport...
MimeMessage mimeMessage = mimeMessages[i];
try {
...
}
catch (Exception ex) {
Object original = (originalMessages != null ? originalMessages[i] : mimeMessage);
failedMessages.put(original, ex);
}
}
}
finally {
try {
if (transport != null) {
transport.close();
}
}
catch (Exception ex) {
if (!failedMessages.isEmpty()) {
throw new MailSendException("Failed to close server connection after message failures", ex,
failedMessages);
}
else {
throw new MailSendException("Failed to close server connection after message sending", ex);
}
}
} if (!failedMessages.isEmpty()) {
throw new MailSendException(failedMessages);
}
}

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

  

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

  1. 解决Spring Boot 从1.x升级到 2.x 后 单点登陆(SSO)问题

    解决Spring Boot 从1.x升级到 2.x 后 单点登陆(SSO)问题   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring S ...

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

    解决spring boot启动报错java.lang.NoClassDefFoundError: ch/qos/logback/classic/Level 学习了:https://blog.csdn. ...

  3. spring boot ---web应用开发-错误处理

    一.错误的处理 方法一:Spring Boot 将所有的错误默认映射到/error, 实现ErrorController @Controller @RequestMapping(value = &qu ...

  4. 如何解决spring boot 项目导入依赖后代码报错问题

    如何解决spring boot 项目导入依赖后代码报错问题 2020-08-15  14:17:18 代码截图如图所示(由于本人问题已经解决,没来得及截图,所以在网上找了一张图片)

  5. 解决一次gitlab因异常关机导致启动失败

    解决一次gitlab因异常关机导致启动失败 目录 解决一次gitlab因异常关机导致启动失败 1. 服务器异常关机 2. gitlab服务 2.1 进入gitlab容器内部 2.2 检查gitlab各 ...

  6. spring boot:方法中使用try...catch导致@Transactional事务无效的解决(spring boot 2.3.4)

    一,方法中使用try...catch导致@Transactional事务无效的解决方法 1,问题的描述: 如果一个方法添加了@Transactional注解声明事务, 而方法内又使用了try catc ...

  7. 解决spring boot中rest接口404,500等错误返回统一的json格式

    在开发rest接口时,我们往往会定义统一的返回格式,列如: { "status": true, "code": 200, "message" ...

  8. spring boot 下 500 404 错误页面处理

    spring boot 作为微服务的便捷框架,在错误页面处理上也有一些新的处理,不同于之前的spring mvc 500的页面处理是比较简单的,用java config或者xml的形式,定义如下的be ...

  9. 解决Spring boot中读取属性配置文件出现中文乱码的问题

    问题描述: 在配置文件application.properties中写了 server.port=8081 server.servlet.context-path=/boy name=张三 age=2 ...

随机推荐

  1. 第十二周(12.01-12.04)----final评论I

    1.  约跑App——nice!:作为final发布讲说的第一组,nice团队很不容易.虽然很早就来到了发布场地,为发布做准备.但是准备上还是有些不足.对于摄像头的不稳定,nice没有很好的解决.在演 ...

  2. Oracle 最新版本变化 转帖

    版本更迭 http://www.sohu.com/a/163264045_505827 Oracle Database的下一个版本将是 Oracle 18. 目的 为了更快的.通过每年的版本发布将新特 ...

  3. 一本通1646GT 考试

    1646:GT 考试 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 阿申准备报名参加 GT 考试,准考证号为 n 位数 X1X2⋯Xn(0≤Xi≤9),他不 ...

  4. [代码]--SQLServer数据库的一些全局变量

    select APP_NAME ( ) as w --当前会话的应用程序 select @@IDENTITY   --返回最后插入的标识值 select USER_NAME()    --返回用户数据 ...

  5. dp乱写1:状态压缩dp(状压dp)炮兵阵地

    https://www.luogu.org/problem/show?pid=2704 题意: 炮兵在地图上的摆放位子只能在平地('P') 炮兵可以攻击上下左右各两格的格子: 而高原('H')上炮兵能 ...

  6. 界面编程之QT的文件操作20180729

    /*******************************************************************************************/ 一.QT文件 ...

  7. vue-devtools/安装vue-devtools

    一.在github上下载压缩包,github下载地址:https://github.com/vuejs/vue-devtools 二.解压到本地的某盘 三.用你的npm中进入该文件夹下 在npm中执行 ...

  8. apigateway-kong(四)负载均衡理论及实现

    负载均衡(Load balancing)是一种计算机网络技术,用来在多个计算机(计算机集群).网络连接.CPU.磁盘驱动器或其他资源中分配负载,以达到最佳化资源使用.最大化吞吐率.最小化响应时间.同时 ...

  9. Stanford机器学习笔记-9. 聚类(K-means算法)

    9. Clustering Content 9. Clustering 9.1 Supervised Learning and Unsupervised Learning 9.2 K-means al ...

  10. LeetCode 4.反转整数

    给定一个 32 位有符号整数,将整数中的数字进行反转. 示例 1: 输入: 123 输出: 321  示例 2: 输入: -123 输出: -321 示例 3: 输入: 120 输出: 21 注意: ...