# 异常捕获

系统中的异常需要统一的捕获,并以统一的格式返给前端,下面来谈一谈如何统一处理异常,本项目使用的第二种处理方式

# @ControllerAdvice

@ControllerAdvice,是 Spring3.2 提供的新注解

  • @ControllerAdvice 是一个 @Component,用于定义@ExceptionHandler,@InitBinder 和@ModelAttribute 方法,适用于所有使用 @RequestMapping 方法。
  • Spring4 之前,@ControllerAdvice 在同一调度的 Servlet 中协助所有控制器。Spring4 已经改变:@ControllerAdvice 支持配置控制器的子集,而默认的行为仍然可以利用。
  • 在 Spring4 中, @ControllerAdvice 通过 annotations(), basePackageClasses(), basePackages()方法定制用于选择控制器子集。

简单来说就是给所有的 Controller 加上统一的 @ExceptionHandler 或 @InitBinder 或 @ModelAttribute 处理。

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理所有不可知的异常
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity handleException(Exception e){
        log.error("服务器异常:{}", e);
        return new ResponseEntity<>("服务器异常,请稍后重试", HttpStatus.BAD_REQUEST);
    }

    /**
     * 处理数组越界异常
     */
    @ExceptionHandler(ArrayIndexOutOfBoundsException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "数组越界")
    public void handleAccessDeniedException(ArrayIndexOutOfBoundsException e){
        log.error("数组越界:{}", e.getMessage());
    }

    /**
     * 业务代码错误
     */
    @ExceptionHandler(value = ServiceException.class)
    public ResponseEntity serviceException(ServiceException e) {
        log.error("业务代码错误:{}", e.getMessage());
        return new ResponseEntity<>("业务代码错误:", HttpStatus.BAD_REQUEST);
    }
}

@RestControllerAdvice 是 @ControllerAdvice 的增强,类似于 @RestController

上面的代码中可以看到通过 @RestControllerAdvice 注解,将上面的三种异常处理加到了所有的 controller 上,这样就能捕获所有能到达 controller 层的异常。

缺点是 Controller 之前的异常无法捕获(比如 Filter)

以上只是 demo 展示,最佳实战请见另一篇博客 spring 统一异常处理

# spring 底层的 API

这里我们采用 spring 更底层的 API 处理,即所有错误都会转发到 /error 路径的 Controller 上。

自定义实现可以参考 spring 的 BasicErrorController 类,本项目的实现为 GlobalErrorController,它捕获到所有的异常后,如果是我们自定义的 ServiceException 则直接返回,如果是前端参数反序列化错误,则返回“参数绑定错误”,其他异常则返回“内部异常”