使用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. HDU 2022 海选女主角

    http://acm.hdu.edu.cn/showproblem.php?pid=2022 Problem Description potato老师虽然很喜欢教书,但是迫于生活压力,不得不想办法在业 ...

  2. Docker in Docker的安装 路不通

    1. 先启动centos 镜像 然后 再docker cp文件 然后再执行安装报错 [root@CentOS75 ~]# docker run -it centos /bin/bash [root@1 ...

  3. clear & file input & reset & file input

    clear & file input & reset & file input Clear <input type="file"> docume ...

  4. delphi 控件的名称怎么不显示了

    选择菜单 Tools--Environment在打开的对话框中选择 Designer 页,选 其中的 Options 选项勾选 Show component captions ,点击 OK即可

  5. java 堆和栈二

    1.数组 整数默认初始化值0 浮点数默认初始化值0.0 布尔类型默认初始化值false 字符类型默认初始化值\u0000 [I@7852e922 [有几个代表是几维数组 I代表是Int类型 @固定格式 ...

  6. 一本通1623Sherlock and His Girlfriend

    1623:Sherlock and His Girlfriend 时间限制: 1000 ms         内存限制: 524288 KB [题目描述] 原题来自:Codeforces Round ...

  7. pgm6

    有个比较有意思的想法是编码理论的反问题是 machine learning,这也是这部分学习的一个收获.这个其实很奇怪,编码理论其实是有 ground truth 的,然后通过编码产生“冗余”,这样才 ...

  8. 「TJOI / HEOI2016」字符串

    「TJOI / HEOI2016」字符串 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为 \(n\) 的字符串 \(s\),和 ...

  9. 洛谷 P1070 道路游戏 解题报告

    P1070 道路游戏 题目描述 小新正在玩一个简单的电脑游戏. 游戏中有一条环形马路,马路上有\(n\)个机器人工厂,两个相邻机器人工厂之间由一小段马路连接.小新以某个机器人工厂为起点,按顺时针顺序依 ...

  10. 前端学习 -- Css -- 浮动

    块元素在文档流中默认垂直排列,所以这个三个div自上至下依次排开,如果希望块元素在页面中水平排列,可以使块元素脱离文档流. 使用float来使元素浮动,从而脱离文档流 可选值: none,默认值,元素 ...