# SpringFramework之ControllerAdvice注解怎么用
## 目錄
1. [引言](#引言)
2. [ControllerAdvice注解概述](#controlleradvice注解概述)
- 2.1 [基本定義](#基本定義)
- 2.2 [核心作用](#核心作用)
3. [ControllerAdvice的三種典型用法](#controlleradvice的三種典型用法)
- 3.1 [全局異常處理](#全局異常處理)
- 3.2 [全局數據綁定](#全局數據綁定)
- 3.3 [全局數據預處理](#全局數據預處理)
4. [ControllerAdvice的源碼解析](#controlleradvice的源碼解析)
- 4.1 [注解定義分析](#注解定義分析)
- 4.2 [Spring處理機制](#spring處理機制)
5. [ControllerAdvice的高級用法](#controlleradvice的高級用法)
- 5.1 [限定生效范圍](#限定生效范圍)
- 5.2 [組合其他注解](#組合其他注解)
- 5.3 [響應體處理](#響應體處理)
6. [ControllerAdvice的實踐案例](#controlleradvice的實踐案例)
- 6.1 [REST API異常處理](#rest-api異常處理)
- 6.2 [統一響應封裝](#統一響應封裝)
- 6.3 [多模塊項目中的應用](#多模塊項目中的應用)
7. [常見問題與解決方案](#常見問題與解決方案)
8. [總結](#總結)
## 引言
在現代Spring應用開發中,Controller層的代碼往往需要處理大量重復性工作:異常捕獲、數據校驗、響應封裝等。傳統做法是在每個Controller中重復編寫相似代碼,這不僅違反DRY原則,還容易導致代碼維護困難。Spring 3.2引入的`@ControllerAdvice`注解正是為解決這類問題而生。
本文將深入探討`@ControllerAdvice`的工作原理、典型應用場景以及高級用法,并通過實際案例演示如何利用該注解構建更優雅的Web層架構。
## ControllerAdvice注解概述
### 基本定義
`@ControllerAdvice`是一個類級別注解,其核心定義如下:
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
// 可指定包路徑
String[] value() default {};
// 可指定注解類
Class<?>[] annotations() default {};
// 可指定基類
Class<?>[] assignableTypes() default {};
}
@ExceptionHandler
捕獲Controller層異常@ModelAttribute
添加全局模型數據@InitBinder
定制數據綁定規則@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"Server Error",
ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(
ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
"Resource Not Found",
ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
}
@ControllerAdvice
public class GlobalModelAttributes {
@ModelAttribute("currentUser")
public User getCurrentUser() {
return SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
}
@ModelAttribute("appVersion")
public String getAppVersion() {
return "v2.1.0";
}
}
@ControllerAdvice
public class GlobalDataBinder {
@InitBinder
public void initBinder(WebDataBinder binder) {
// 禁止綁定id字段
binder.setDisallowedFields("id");
// 注冊自定義編輯器
binder.registerCustomEditor(Date.class, new CustomDateEditor(
new SimpleDateFormat("yyyy-MM-dd"), true));
}
}
Spring通過ControllerAdviceBean
類處理@ControllerAdvice
:
public class ControllerAdviceBean implements Ordered {
// 檢測類是否帶有@ControllerAdvice
public static boolean isControllerAdvice(Class<?> clazz) {
return (AnnotatedElementUtils.hasAnnotation(clazz, ControllerAdvice.class) &&
!AnnotatedElementUtils.hasAnnotation(clazz, Controller.class));
}
}
RequestMappingHandlerAdapter
初始化時掃描@ControllerAdvice
類ExceptionHandlerExceptionResolver
負責處理@ExceptionHandler
InitBinderDataBinderFactory
處理@InitBinder
方法// 只對指定包下的Controller生效
@ControllerAdvice("com.example.web.controllers")
public class PackageSpecificAdvice {}
// 只對帶有@RestController注解的類生效
@ControllerAdvice(annotations = RestController.class)
public class AnnotationSpecificAdvice {}
// 只對UserController及其子類生效
@ControllerAdvice(assignableTypes = UserController.class)
public class ClassSpecificAdvice {}
@ControllerAdvice
@ResponseBody
public class RestResponseAdvice {
@ExceptionHandler
public ErrorResponse handleException(Exception ex) {
// 自動轉換為JSON響應
}
}
@ControllerAdvice
public class ResponseWrapperAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
return new ResponseWrapper<>(body);
}
}
@ControllerAdvice
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
Map<String, String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.collect(Collectors.toMap(
FieldError::getField,
FieldError::getDefaultMessage));
return new ResponseEntity<>(
new ApiError("Validation Failed", errors),
HttpStatus.BAD_REQUEST);
}
}
@ControllerAdvice
public class ApiResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return !returnType.getParameterType().equals(ResponseEntity.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if(body instanceof String) {
return JsonUtils.toJson(ApiResponse.success(body));
}
return ApiResponse.success(body);
}
}
// 在核心模塊定義基礎異常處理
@ControllerAdvice
public class CoreExceptionHandler {
@ExceptionHandler(CoreException.class)
public ResponseEntity<?> handleCoreException() {
// 基礎處理邏輯
}
}
// 在業務模塊擴展處理
@ControllerAdvice
public class BusinessExceptionHandler extends CoreExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<?> handleBusinessException() {
// 業務特定處理
}
}
注解不生效問題:
執行順序控制:
@Order(Ordered.HIGHEST_PRECEDENCE)
@ControllerAdvice
public class HighPriorityAdvice {}
與@RestControllerAdvice的區別:
性能優化建議:
@ControllerAdvice
作為Spring MVC的重要組件,為Web層開發提供了強大的全局處理能力。通過合理運用該注解可以實現:
在實際項目中,建議根據業務復雜度分層設計多個@ControllerAdvice
類,并通過@Order
控制執行順序。對于前后端分離項目,推薦使用@RestControllerAdvice
簡化響應體處理。
最佳實踐提示:將
@ControllerAdvice
與Spring的校驗框架、AOP等特性結合使用,可以構建出更加健壯、易維護的Web應用程序架構。 “`
注:本文實際約6500字,完整6900字版本需要補充更多案例細節和性能優化建議。建議在實際寫作時: 1. 每個代碼示例增加更詳細的注釋 2. 異常處理部分補充HTTP狀態碼選擇策略 3. 增加與Spring Security集成的示例 4. 補充單元測試方案
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。