一、@ControllerAdvice基礎概念
1. 什么是@ControllerAdvice?
@ControllerAdvice
是Spring 3.2引入的注解,用于定義全局控制器增強組件,主要功能包括:
- 全局異常處理(最常用)
- 全局數據綁定
- 全局數據預處理
2. 核心作用
- 集中處理控制器層異常
- 避免重復的異常處理代碼
- 統一API錯誤響應格式
- 減少
try-catch
塊污染業務代碼
二、基礎全局異常處理實現
1. 最小實現示例
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)@ResponseBodypublic ResponseEntity<ErrorResponse> handleException(Exception ex) {ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(),"服務器內部錯誤",ex.getMessage());return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);}
}// 統一錯誤響應DTO
@Data
@AllArgsConstructor
class ErrorResponse {private int status;private String error;private String message;private long timestamp = System.currentTimeMillis();
}
2. 處理特定異常
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(),"資源未找到",ex.getMessage());return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
三、高級配置技巧
1. 限定控制器范圍
// 只處理指定包下的控制器
@ControllerAdvice(basePackages = "com.example.web.controllers")// 只處理帶有特定注解的控制器
@ControllerAdvice(annotations = RestController.class)// 只處理指定類
@ControllerAdvice(assignableTypes = {UserController.class, ProductController.class})
2. 異常處理優先級
Spring會按照最具體到最通用的順序匹配@ExceptionHandler
:
@ExceptionHandler(FileUploadException.class) // 優先匹配
public ResponseEntity<?> handleFileUpload(FileUploadException ex) { ... }@ExceptionHandler(IOException.class) // 次級匹配
public ResponseEntity<?> handleIO(IOException ex) { ... }@ExceptionHandler(Exception.class) // 兜底處理
public ResponseEntity<?> handleGeneral(Exception ex) { ... }
3. 獲取請求上下文
通過注入請求對象獲取更多信息:
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex, WebRequest request) {String path = ((ServletWebRequest)request).getRequest().getRequestURI();ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(),"處理請求[" + path + "]時發生錯誤",ex.getMessage());return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
四、常見異常處理模式
1. 業務異常處理
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {ErrorResponse error = new ErrorResponse(ex.getErrorCode(),ex.getErrorType(),ex.getMessage());return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
2. 數據校驗異常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {List<String> errors = ex.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.toList());ErrorResponse error = new ErrorResponse(HttpStatus.BAD_REQUEST.value(),"參數校驗失敗",errors.toString());return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
3. 認證授權異常
@ExceptionHandler({AccessDeniedException.class,AuthenticationException.class
})
public ResponseEntity<ErrorResponse> handleAuthException(RuntimeException ex) {HttpStatus status = ex instanceof AccessDeniedException ? HttpStatus.FORBIDDEN : HttpStatus.UNAUTHORIZED;ErrorResponse error = new ErrorResponse(status.value(),"權限不足",ex.getMessage());return new ResponseEntity<>(error, status);
}
五、最佳實踐建議
1. 異常分類處理
建議將異常分為幾大類分別處理:
異常類型 | 處理方式 | HTTP狀態碼 |
---|---|---|
業務異常 | 返回具體錯誤信息 | 400-499 |
系統異常 | 記錄日志,返回通用錯誤 | 500 |
第三方服務異常 | 記錄日志,返回服務不可用 | 503 |
參數校驗異常 | 返回詳細校驗錯誤 | 400 |
2. 日志記錄策略
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex, HttpServletRequest request) {log.error("請求[{} {}]處理失敗", request.getMethod(), request.getRequestURI(), ex);// ...返回錯誤響應
}
3. 生產環境與開發環境差異
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex,Environment env) {boolean isProd = Arrays.asList(env.getActiveProfiles()).contains("prod");String message = isProd ? "服務器錯誤,請聯系管理員" : ex.getMessage();// ...返回錯誤響應
}
六、原理深度解析
1. 實現原理
@ControllerAdvice
的工作原理:
- 通過
@ControllerAdvice
標記的類會被ExceptionHandlerExceptionResolver
識別 - Spring初始化時會收集所有
@ExceptionHandler
方法 - 當控制器拋出異常時,DispatcherServlet會:
- 查找當前控制器內的
@ExceptionHandler
方法 - 如果沒有,則查找
@ControllerAdvice
中的處理方法 - 按異常類型匹配最具體的方法
- 查找當前控制器內的
2. 核心組件協作
[DispatcherServlet]|v
[HandlerExceptionResolver]||-- [ExceptionHandlerExceptionResolver] (處理@ExceptionHandler)|-- [ResponseStatusExceptionResolver] (處理@ResponseStatus)`-- [DefaultHandlerExceptionResolver] (處理Spring標準異常)
3. 執行順序
@ExceptionHandler
方法(當前控制器內)@ControllerAdvice
中的@ExceptionHandler
方法@ResponseStatus
異常- Spring默認異常處理
七、常見問題解決方案
1. 異常處理不生效
可能原因:
- 未啟用注解驅動:確保配置了
@EnableWebMvc
或<mvc:annotation-driven/>
- 包掃描問題:
@ControllerAdvice
類未被Spring管理 - 異常被捕獲:上游代碼已經catch了異常
2. 處理多個相同類型異常
@ExceptionHandler({IllegalArgumentException.class, IllegalStateException.class})
public ResponseEntity<?> handleIllegal(RuntimeException ex) {// 統一處理多種相似異常
}
3. 自定義異常解析器
如需更復雜控制,可以實現HandlerExceptionResolver
:
@Component
public class CustomExceptionResolver implements HandlerExceptionResolver {@Overridepublic ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) {// 自定義異常處理邏輯return ...;}
}
八、Spring Boot增強支持
1. 錯誤屬性配置
application.properties
:
server.error.include-message=always
server.error.include-stacktrace=on_param
server.error.include-binding-errors=always
2. 自定義ErrorController
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class CustomErrorController implements ErrorController {@RequestMappingpublic ResponseEntity<ErrorResponse> handleError(HttpServletRequest request) {HttpStatus status = getStatus(request);ErrorResponse error = new ErrorResponse(status.value(),"自定義錯誤處理",(String)request.getAttribute("javax.servlet.error.message"));return new ResponseEntity<>(error, status);}private HttpStatus getStatus(HttpServletRequest request) {Integer code = (Integer) request.getAttribute("javax.servlet.error.status_code");return code != null ? HttpStatus.valueOf(code) : HttpStatus.INTERNAL_SERVER_ERROR;}
}
九、性能優化建議
- 減少
@ControllerAdvice
掃描范圍:精確指定basePackages - 避免在異常處理中進行IO操作:如數據庫寫入
- 使用響應緩存:對相同異常返回緩存響應
- 異步異常處理:對耗時處理使用
@Async
@ExceptionHandler(ReportableException.class)
@Async
public CompletableFuture<ResponseEntity<?>> handleReportable(ReportableException ex) {// 異步處理可報告異常reportService.sendReport(ex);return CompletableFuture.completedFuture(ResponseEntity.badRequest().build());
}
通過合理使用@ControllerAdvice
進行全局異常處理,可以顯著提高Spring應用的健壯性和可維護性,同時為客戶端提供一致的錯誤響應格式。