Acegi 將「業務攔截」與「異常處理」分離:FilterSecurityInterceptor 專職決策、拋出異常;ExceptionTranslationFilter 專職捕獲、轉換為 HTTP 回應。這種職責分離思想極具參考價值。
FilterSecurityInterceptor。AccessDeniedException 或 AuthenticationException。ExceptionTranslationFilter 捕獲,委派 AuthenticationEntryPoint(導向登入)或 AccessDeniedHandler(403 頁面)。protected String sendAlertMessage(String to, String subject, String msg, String from) {
SimpleMailMessage mail = createMessage(to, subject, msg, from);
try {
mailEngine.sendEmail(mail);
} catch (MailException ex) {
log.error(ex.getMessage()); // 僅記錄
return ex.getMessage(); // 回傳 String,呼叫端失去型別資訊
}
return "1";
}
缺點:
MailAuthenticationException、MailSendException 分流處理。// 1. 定義業務異常層級
public class MailSendFailedException extends RuntimeException {
private final MailException cause;
private final String fromRealName;
// 建構子、Getter...
}
// 2. 底層僅轉換,不吞噬
public void send(SimpleMailMessage mail) {
try {
mailEngine.sendEmail(mail);
} catch (MailException ex) {
throw new MailSendFailedException("郵件發送失敗", ex, fromRealName);
}
}
// 3. 控制層/切面統一處理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MailSendFailedException.class)
public ResponseEntity<ErrorResponse> handle(MailSendFailedException ex) {
String msg = String.format("由 %s 發送給 %s 的郵件因 %s 失敗",
ex.getFromRealName(), ex.getTo(), ex.getCause().getClass().getSimpleName());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse(msg));
}
}
框架級程式碼的異常處理往往是最佳範本。閱讀 Spring Security、Netty、Dubbo 等原始碼的異常體系,能顯著提升架構設計能力。