@ControllerAdvice注解:

  • 不写参数:所有Controller
  • 参数为包名:包下的所有Controller,可指定多个如:@ControllerAdvice(basePackages={"cn.ken.test1", "cn.ken.test2"})
  • 参数为注解:可以自定义注解后匹配所有加了该注解的Controller,如:@ControllerAdvice(annotations={MyAnnotation.class})

此注解是一个在类上声明的注解,是aop思想的一种实现,主要通过配合@ExceptionHandler、@InitBinder 或 @ModelAttribute这三个注解以实现全局异常处理、全局数据绑定和全局数据预处理

@ExceptionHandler注解指定要捕获的异常类型

@ControllerAdvice
public class AllControllerAdvice {
    private static Logger logger = LoggerFactory.getLogger(AllControllerAdvice.class);

    /**
     * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
    }

    /**
     * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
     */
    @ModelAttribute
    public void addAttributes(Model model) {
    }


    /**
     * 捕捉shiro的异常
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ShiroException.class)
    @ResponseBody
    public ResponseModel<String> handleShiroException(ShiroException e) {
        return ResponseHelper.failedWith(null, CodeEnum.IDENTIFICATION_ERROR.getCode(),CodeEnum.IDENTIFICATION_ERROR.getMsg());
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(AuthenticationException.class)
    @ResponseBody
    public ResponseModel<String> handleShiroException(AuthenticationException e) {
        return ResponseHelper.failedWith(null, CodeEnum.IDENTIFICATION_ERROR.getCode(),CodeEnum.IDENTIFICATION_ERROR.getMsg());
    }

    /**
     * 捕捉BusinessException自定义抛出的异常
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public ResponseModel handleBusinessException(BusinessException e) {
        String message = e.getMessage();
        if(message.indexOf(":--:") > 0){
            String[] split = message.split(":--:");
            return ResponseHelper.failedWith(null,split[1],split[0]);
        }
        return ResponseHelper.failedWith(null,CodeEnum.DATA_ERROR.getCode(),message);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(TemplateInputException.class)
    @ResponseBody
    public ResponseModel<String> handleTemplateInputException(TemplateInputException e) {
        return ResponseHelper.failed2Message(CodeEnum.USER_NO_PERMITION.getMsg());
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = ParamJsonException.class)
    @ResponseBody
    public ResponseModel<String> handleParamJsonException(Exception e) {
        if(e instanceof ParamJsonException) {
            logger.info("参数错误:"+e.getMessage());
            return ResponseHelper.failed2Message("参数错误:"+ e.getMessage());
        }
        return ResponseHelper.failedWith(null,CodeEnum.PARAM_ERROR.getCode(),e.getMessage());
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    @ResponseBody
    public ResponseModel<String> handleParamJsonException(HttpMessageNotReadableException e) {
        if(e instanceof HttpMessageNotReadableException) {
            logger.info("参数错误:"+e.getMessage());
            return ResponseHelper.failed2Message("参数错误:"+ e.getMessage());
        }
        return ResponseHelper.failedWith(null,CodeEnum.PARAM_ERROR.getCode(),e.getMessage());
    }

    /**
     * 全局异常捕捉处理
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public ResponseModel<String> errorHandler(Exception ex) {
        ex.printStackTrace();
        logger.error("接口出现严重异常:{}", ex.getMessage());
        return ResponseHelper.failed2Message(CodeEnum.ERROR.getMsg());
    }

}

可在类下定义多个@ExceptionHandler以对不同异常进行不同的处理

ExceptionHandler的处理顺序是由异常匹配度来决定的,首先找到可以匹配异常的所有ExceptionHandler,然后对其进行排序(深度比较器,通过递归不停地判断父异常是否为目标异常来取得最终的深度),取深度最小,即匹配度最高的那个