解决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 ...
随机推荐
- node解决跨域问题
app.all('*', function(req, res, next) { res.header("Access-Control-Allow-Origin", "*& ...
- SQLSERVER 设置自动备份数据库
1. SQLSERVER 简单的设置 计划任务 进行 备份数据库的操作. 首先需要打开 一些设置 执行 命令如下: sp_configure ; GO RECONFIGURE; GO sp_confi ...
- MyBatis传入多个参数的问题(转)
一.单个参数: public List<XXBean> getXXBeanList(String xxCode); <select id="getXXXBeanList&q ...
- linux虚拟机安装中出现的问题
当虚拟机加载镜像时,出现下面的错误: vmware 已将该虚拟机配置为使用 64 位客户机操作系统.但是,无法执行 64 位操作. 错误提示:已将该虚拟机配置为使用 64 位客户机操作系统.但是,无法 ...
- 一本通1633【例 3】Sumdiv
1633:[例 3]Sumdiv 时间限制: 1000 ms 内存限制: 524288 KB [题目描述] 原题来自:Romania OI 2002 求 ABAB 的所有约数之和 mo ...
- 浅谈Java中的深克隆和浅克隆(阿里面试)
在最近的秋招中,阿里和多益网络都问到了这个问题,虽然很简单,但是我还是想总结一下,感兴趣的可以看一下我的个人博客网站(Spring+MyBatis+redis+nginx+mysql)(适合菜鸟),最 ...
- 论如何优雅的用bitset来求四维偏序
四维偏序.. 就是给你一个四维集合.再给你一些询问,请你求出a[i].x1<=ask.x1&&a[i].x2<=ask.x2&&a[i].x3<=as ...
- NOIP2018 No regrets youth
NOIP2018在即,20181009总结一些易错的知识点和解题方法 ——by ljc20020730 HGOI NOIP2018 No regrets youth ! NOIP2018 No reg ...
- BZOJ1185 [HNOI2007]最小矩形覆盖 【旋转卡壳】
题目链接 BZOJ1185 题解 最小矩形一定有一条边在凸包上,枚举这条边,然后旋转卡壳维护另外三个端点即可 计算几何细节极多 维护另外三个端点尽量不在这条边上,意味着左端点尽量靠后,右端点尽量靠前, ...
- aws上部署zabbix3.4
三台机器 10.0.0.149 AmazonLinux2.0 zabbix-server zabbix-agent 10.0.1.61 CentOS6.9 zabbix-agent 10.0.1.11 ...