您的位置:首页 > 其它

@ControllerAdvice @ExceptionHandler 全局处理 Controller 层异常

2018-03-26 13:02 337 查看

前言

通过@ControllerAdvice。我们可以将对于控制器的全局配置放置在同一个位置,注解了@ControllerAdvice的类的方法可以使用@ExceptionHandler,@InitBinder,@ModelAttribute注解到方法上,这对所有注解了@RequestMapping的控制器内的方法有效

@ExceptionHandler:用于全局处理控制器里面的异常。

@InitBinder:用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。

@ModelAttribute:@ModelAttribute本来的作用是绑定键值对到Model里,此处是让全局的@RequestMapping都能获得在此处设置的键值对。

对于与数据库相关的 Spring MVC 项目,我们通常会把 事务 配置在 Service层,当数据库操作失败时让 Service 层抛出运行时异常,Spring 事物管理器就会进行回滚。

如此一来,我们的 Controller 层就不得不进行 try-catch Service 层的异常,否则会返回一些不友好的错误信息到客户端。但是,Controller 层每个方法体都写一些模板化的 try-catch 的代码,很难看也难维护,特别是还需要对 Service 层的不同异常进行不同处理的时候。例如以下 Controller 方法代码(非常难看且冗余):

/**
* 手动处理 Service 层异常和数据校验异常的示例
* @param dog
* @param errors
* @return
*/
@PostMapping(value = "")
AppResponse add(@Validated(Add.class) @RequestBody Dog dog, Errors errors){
AppResponse resp = new AppResponse();
try {
// 数据校验
BSUtil.controllerValidate(errors);

// 执行业务
Dog newDog = dogService.save(dog);

// 返回数据
resp.setData(newDog);

}catch (BusinessException e){
LOGGER.error(e.getMessage(), e);
resp.setFail(e.getMessage());
}catch (Exception e){
LOGGER.error(e.getMessage(), e);
resp.setFail("操作失败!");
}
return resp;
}


本文讲解使用 @ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理,只要设计得当,就再也不用在 Controller 层进行 try-catch 了!而且,@Validated 校验器注解的异常,也可以一起处理,无需手动判断绑定校验结果 BindingResult/Errors 了!

优缺点

优点:将 Controller 层的异常和数据校验的异常进行统一处理,减少模板代码,减少编码量,提升扩展性和可维护性。

缺点:只能处理 Controller 层未捕获(往外抛)的异常,对于 Interceptor(拦截器)层的异常,Spring 框架层的异常,就无能为力了。

基本使用示例

@ControllerAdvice 注解定义全局异常处理类

@ControllerAdvice
public class GlobalExceptionHandler {
}


请确保此 GlobalExceptionHandler 类能被扫描到并装载进 Spring 容器中。

@ExceptionHandler 注解声明异常处理方法

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
@ResponseBody
String handleException(){
return "Exception Deal!";
}
}


方法 handleException() 就会处理所有 Controller 层抛出的 Exception 及其子类的异常,这是最基本的用法了。

被 @ExceptionHandler 注解的方法的参数列表里,还可以声明很多种类型的参数,其原型如下

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {

/**
* Exceptions handled by the annotated method. If empty, will default to any
* exceptions listed i
4000
n the method argument list.
*/
Class<? extends Throwable>[] value() default {};

}


如果 @ExceptionHandler 注解中未声明要处理的异常类型,则默认为参数列表中的异常类型。所以上面的写法,还可以写成这样:

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler()
@ResponseBody
String handleException(Exception e){
return "Exception Deal! " + e.getMessage();
}
}


参数对象就是 Controller 层抛出的异常对象!

常见的例子

/**
* Created by kinginblue on 2017/4/10.
* @ControllerAdvice + @ExceptionHandler 实现全局的 Controller 层的异常处理
*/
@ControllerAdvice
public class GlobalExceptionHandler {

private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

/**
* 处理所有不可知的异常
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseBody
AppResponse handleException(Exception e){
LOGGER.error(e.getMessage(), e);

AppResponse response = new AppResponse();
response.setFail("操作失败!");
return response;
}

/**
* 处理所有业务异常
* @param e
* @return
*/
@ExceptionHandler(BusinessException.class)
@ResponseBody
AppResponse handleBusinessException(BusinessException e){
LOGGER.error(e.getMessage(), e);

AppResponse response = new AppResponse();
response.setFail(e.getMessage());
return response;
}
}


参考:https://blog.csdn.net/kinginblue/article/details/70186586
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐