解决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方法
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部分收件人错误导致发送失败的问题的更多相关文章
- 解决Spring Boot 从1.x升级到 2.x 后 单点登陆(SSO)问题
解决Spring Boot 从1.x升级到 2.x 后 单点登陆(SSO)问题 在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring S ...
- 解决spring boot启动报错java.lang.NoClassDefFoundError: ch/qos/logback/classic/Level
解决spring boot启动报错java.lang.NoClassDefFoundError: ch/qos/logback/classic/Level 学习了:https://blog.csdn. ...
- spring boot ---web应用开发-错误处理
一.错误的处理 方法一:Spring Boot 将所有的错误默认映射到/error, 实现ErrorController @Controller @RequestMapping(value = &qu ...
- 如何解决spring boot 项目导入依赖后代码报错问题
如何解决spring boot 项目导入依赖后代码报错问题 2020-08-15 14:17:18 代码截图如图所示(由于本人问题已经解决,没来得及截图,所以在网上找了一张图片)
- 解决一次gitlab因异常关机导致启动失败
解决一次gitlab因异常关机导致启动失败 目录 解决一次gitlab因异常关机导致启动失败 1. 服务器异常关机 2. gitlab服务 2.1 进入gitlab容器内部 2.2 检查gitlab各 ...
- spring boot:方法中使用try...catch导致@Transactional事务无效的解决(spring boot 2.3.4)
一,方法中使用try...catch导致@Transactional事务无效的解决方法 1,问题的描述: 如果一个方法添加了@Transactional注解声明事务, 而方法内又使用了try catc ...
- 解决spring boot中rest接口404,500等错误返回统一的json格式
在开发rest接口时,我们往往会定义统一的返回格式,列如: { "status": true, "code": 200, "message" ...
- spring boot 下 500 404 错误页面处理
spring boot 作为微服务的便捷框架,在错误页面处理上也有一些新的处理,不同于之前的spring mvc 500的页面处理是比较简单的,用java config或者xml的形式,定义如下的be ...
- 解决Spring boot中读取属性配置文件出现中文乱码的问题
问题描述: 在配置文件application.properties中写了 server.port=8081 server.servlet.context-path=/boy name=张三 age=2 ...
随机推荐
- Linux命令(二十) 显示系统内存状态 free
一.命令简介 free 命令会显示内存的使用情况,包括实体内存,虚拟的交换文件内存.共享内存区段,以及系统核心使用的缓冲区等. 二.参数说明 -b 以Byte为单位显示内存使用情况 -K 以KB为单位 ...
- 【转帖】 redis 命令 From https://www.cnblogs.com/zhouweidong/p/7550717.html
redis命令详解 redis中添加key value元素:set key value; 获取元素:get key ; redis中添加集合:lpush key value1 va ...
- SpringBoot 3.SpringBoot 整合 MyBatis 逆向工程以及 MyBatis 通用 Mapper
一.添加所需依赖,当前完整的pom文件如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=&qu ...
- 使用fiddler的过滤条件
使用fiddler抓包的时候经常一下子显示很多的记录,看的眼花缭乱,需要这时候需要使用过滤条件来帮助你,一般常用的有三种过滤条件: 1.域名过滤,只显示特定域名的记录: *.baidu.com表示所有 ...
- 实现AJAX的基本步骤
实现AJAX的基本步骤 要完整实现一个AJAX异步调用和局部刷新,通常需要以下几个步骤: (1)创建XMLHttpRequest对象,也就是创建一个异步调用对象. (2)创建一个新的HTTP请求,并指 ...
- 菜菜小问题——python中print函数 以及单引号、双引号、三引号
直接面对——引号,就是为了保证打印出来的东东符合预期 如:print("小菜菜") 结果是: .================1========================= ...
- Day25-博客系统
1. 搭建环境请参考:http://www.cnblogs.com/momo8238/p/7508677.html 2.创建3张表备用 models.py from django.db import ...
- 【题解】 [ZJOI2006]书架 (Splay)
懒得复制,戳我戳我 Solution: 还是一个\(Splay\),我们只用多存一个值\(rad\)来维护二叉树,然后用数组存下每个书对应的值是多少 \(Top\)操作,我是把\(s\)旋转到根节点, ...
- VirtualBox 文件共享及网络配置技巧
文件共享 1. 安装增强功能 2. 分配数据空间 这样就可以在virtualbox里访问本地的数据了: 网络配置
- 数位dp——奏响数字数位的美妙乐章
数位dp:处理数字数位关系的一种dp方式. 一般的题目特征十分明显: 1.一般和数字本身有很大关系. 2.一般求数字在区间L,R中的一些信息 3.L,R一般很大,通常能达到long long级别. d ...