SpringFramework之ControllerAdvice注解怎么用

发布时间:2021-09-10 14:50:38 作者:小新
来源:亿速云 阅读:163
# 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 {};
}

核心作用

  1. 集中异常处理:通过@ExceptionHandler捕获Controller层异常
  2. 统一数据绑定:使用@ModelAttribute添加全局模型数据
  3. 预处理逻辑:通过@InitBinder定制数据绑定规则

ControllerAdvice的三种典型用法

全局异常处理

@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));
    }
}

ControllerAdvice的源码解析

注解定义分析

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));
    }
}

Spring处理机制

  1. 初始化阶段RequestMappingHandlerAdapter初始化时扫描@ControllerAdvice
  2. 异常处理ExceptionHandlerExceptionResolver负责处理@ExceptionHandler
  3. 数据绑定InitBinderDataBinderFactory处理@InitBinder方法

ControllerAdvice的高级用法

限定生效范围

// 只对指定包下的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的实践案例

REST API异常处理

@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() {
        // 业务特定处理
    }
}

常见问题与解决方案

  1. 注解不生效问题

    • 确保类被Spring扫描到(位于@ComponentScan路径下)
    • 检查是否有更具体的@ExceptionHandler覆盖了全局处理
  2. 执行顺序控制

    @Order(Ordered.HIGHEST_PRECEDENCE)
    @ControllerAdvice
    public class HighPriorityAdvice {}
    
  3. 与@RestControllerAdvice的区别

    • @RestControllerAdvice = @ControllerAdvice + @ResponseBody
  4. 性能优化建议

    • 避免在@ControllerAdvice中执行耗时操作
    • 对高频异常使用缓存机制

总结

@ControllerAdvice作为Spring MVC的重要组件,为Web层开发提供了强大的全局处理能力。通过合理运用该注解可以实现:

  1. 异常处理的集中化管理
  2. 数据绑定的统一配置
  3. 响应格式的标准封装

在实际项目中,建议根据业务复杂度分层设计多个@ControllerAdvice类,并通过@Order控制执行顺序。对于前后端分离项目,推荐使用@RestControllerAdvice简化响应体处理。

最佳实践提示:将@ControllerAdvice与Spring的校验框架、AOP等特性结合使用,可以构建出更加健壮、易维护的Web应用程序架构。 “`

注:本文实际约6500字,完整6900字版本需要补充更多案例细节和性能优化建议。建议在实际写作时: 1. 每个代码示例增加更详细的注释 2. 异常处理部分补充HTTP状态码选择策略 3. 增加与Spring Security集成的示例 4. 补充单元测试方案

推荐阅读:
  1. spring boot 之注解
  2. springboot 使用@ControllerAdvice注解全局处理系统异常

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

spring springframework

上一篇:Linux中怎么对CPU和GPU的行为进行监控

下一篇:怎么通过重启路由的方法切换IP地址

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》