使用spring boot通常使用spring-boot-starter-mail进行邮件的发送。当进行邮件群发的话,如果一个收件人的地址错误,会导致所有邮件都发送失败。因此我们需要在邮件发送失败的时候把错误的收件人移除,重新发送。
当邮件发送失败的时候会抛出MailSendException,异常信息中包含错误的收件人信息。
主要代码如下:
private void sendMail(List mailList, MimeMessageHelper message){
try{
this.mailSender.send(message.getMimeMessage());
}catch (MailSendException e){
Set 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) throwsMailException {2 Map 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 newMailSendException(failedMessages);37 }38 }
View Code
当邮件发送过程中遇到异常会保存到failedMessages中,我们需要从中解析收件人无效导致的异常。
接着继续看源码SMTPTransport的rcptTo方法,会去校验每个收件人,通过向服务器发送RCPT TO:,根据响应码来判断收件人是否有效。
protected void rcptTo() throwsMessagingException {
List valid = newArrayList();
List validUnsent = newArrayList();
List invalid = newArrayList();
...intk;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;
}intlrc;if(sendFailed) {this.invalidAddr = newAddress[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 getInvalidAddress(MailSendException e){
Set 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;
}