我正在编写一个管理器来使用
javax.mail
库处理发送电子邮件。构建 Message
时,我使用方法调用者提供的值多次调用 set
方法:
// MailBuilder: builder class for containing various email-related values
public void sendEmails(MailBuilder mailBuilder) {
Message msg = new SMTPMessage(Session.getInstance(presetProperties));
try {
msg.setContent(mailBuilder.body);
msg.setRecipients(RecipientType.TO, mailBuilder.to);
msg.setRecipients(RecipientType.CC, mailBuilder.cc);
msg.setRecipients(RecipientType.BCC, mailBuilder.bcc);
msg.setSubject(mailBuilder.subject);
} catch (MessagingException e) {
throw new IllegalArgumentException("Malformed email", e);
}
// ...send email
}
如果任何
set
方法抛出异常,我想将其包装在 IllegalArgumentException
中以发送给调用者。但是,如果我将它们全部包装在一个 try
块中,如果 setContent
抛出异常,则不会调用其他方法,并且这些方法可能发生的任何错误也不会传达给调用者。我知道我可以像这样将 try-catch 块链接在一起来收集可能被抑制的异常,但它非常笨重:
MessagingException collectedExceptions = null;
try {
msg.setContent(mailBuilder.body)
} catch (MessagingException e) {
if (collectedExceptions == null) collectedExceptions = e;
else collectedExceptions.addSuppressed(e);
}
try {
msg.setSubject(mailBuilder.subject);
} catch (MessagingException e) {
if (collectedExceptions == null) collectedExceptions = e;
else collectedExceptions.addSuppressed(e);
}
// repeat for other methods
if (collectedExceptions != null) throw collectedExceptions;
是否有一种更简洁的方法来使用多个方法,并且仅在任意数量的方法末尾抛出异常?
我不知道为什么你需要调用每一个方法,即使一个方法失败了。至于你的问题,看来你担心的是太多的样板文件。一种解决方案是将您的 try-catch 委托给另一个方法并传递一个列表来收集异常。这是一个例子:
public void sendEmails(MailBuilder mailBuilder) {
Message msg = new SMTPMessage(Session.getInstance(presetProperties));
List<Throwable> exceptions = new ArrayList<Throwable>(5);
tryInvoke(() -> msg.setContent(mailBuilder.body), exceptions);
tryInvoke(() -> msg.setRecipients(RecipientType.TO, mailBuilder.to), exceptions);
tryInvoke(() -> msg.setRecipients(RecipientType.CC, mailBuilder.cc), exceptions);
tryInvoke(() -> msg.setRecipients(RecipientType.BCC, mailBuilder.bcc), exceptions);
tryInvoke(() -> msg.setSubject(mailBuilder.subject), exceptions);
if (!exceptions.isEmpty())
throw new IllegalArgumentException(
exceptions.stream().map(Throwable::getMessage).collect(Collectors.joining(", ")));
}
public static void tryInvoke(ThrowingRunnable<?> callable, List<Throwable> exceptions) {
try {
callable.run();
} catch (Throwable e) {
exceptions.add(e);
}
}
@FunctionalInterface
public interface ThrowingRunnable<E extends Throwable> {
void run() throws E;
}