我们知道,系统中异常包括:编译时异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。在开发中,不管是dao层、service层还是controller层,都有可能抛出异常,在springmvc中,能将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。这篇博文主要总结一下SpringMVC中如何统一处理异常。
异常处理思路
首先来看一下在springmvc中,异常处理的思路
Spring MVC处理异常有4种方式:
(1)使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver;
(2)实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器;
(3)使用@ExceptionHandler注解实现异常处理;
(4)使用@ControllerAdvice + @ExceptionHandler
下面使用 @ControllerAdvice + @ExceptionHandler来实现
通过 @ControllerAdvice 注解,我们可以在一个地方对所有 @Controller 注解的控制器进行管理。
注解了 @ControllerAdvice 的类的方法可以使用 @ExceptionHandler、 @InitBinder、 @ModelAttribute 注解到方法上,这对所有注解了 @RequestMapping 的控制器内的方法都有效。
1:@ExceptionHandler:用于捕获所有控制器里面的异常,并进行处理。
2:@InitBinder:用来设置 WebDataBinder,WebDataBinder 用来自动绑定前台请求参数到 Model 中。
3:@ModelAttribute:@ModelAttribute 本来的作用是绑定键值对到 Model 里,此处是让全局的@RequestMapping 都能获得在此处设置的键值对。
本文使用 @ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理。只要设计得当,就再也不用在 Controller 层进行 try-catch 了!
一、经典案例
需求:希望通过全局统一的异常处理将自定义错误码以json的形式发送给前端。
1、统一返回结果类 ApiResult
首先,定义一个统一结果返回类,最终需要将这个结果类的内容返回给前端。:
/**
* Api统一的返回结果类
*/
public class ApiResult {
/**
* 结果码
*/
private String code;
/**
* 结果码描述
*/
private String msg;
public ApiResult() {
}
public ApiResult(ResultCode resultCode) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
}
/**
* 生成一个ApiResult对象, 并返回
*
* @param resultCode
* @return
*/
public static ApiResult of(ResultCode resultCode) {
return new ApiResult(resultCode);
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "ApiResult{" +
"code='" + code + '\'' +
", msg='" + msg + '\'' +
'}';
}
}
2、错误码枚举类 ResultCode
有了 ApiResult
,接下来需要定义一个枚举类, 来包含所有自定义的结果码。
/**
* 错误码
*/
public enum ResultCode {
SUCCESS("0", "success"),
UNKNOWN_ERROR("0x10001", "unkonwn error"),
USERNAME_ERROR("0x10002", "username error or does not exist"),
PASSWORD_ERROR("0x10003", "password error"),
USERNAME_EMPTY("0x10004", "username can not be empty");
private String code;
private String msg;
ResultCode(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
3、自定义业务异常类 BusinessRuntimeException
接下来需要定义我们自己的业务异常类,以后和业务相关的异常通通抛出这个异常类,我们将错误码枚举变量的值存于其中。
/**
* 自定义业务异常
*/
public class BusinessRuntimeException extends RuntimeException{
private String code;
private String msg;
private ResultCode resultCode;
public BusinessRuntimeException(ResultCode resultCode) {
super(resultCode.getMsg());
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
this.resultCode = resultCode;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public ResultCode getResultCode() {
return resultCode;
}
public void setResultCode(ResultCode resultCode) {
this.resultCode = resultCode;
}
}
4、全局异常处理类 GlobalExceptionResolver
最后便是定义全局异常处理类。
1:通过 @ControllerAdvice 指定该类为 Controller 增强类。
2:通过 @ExceptionHandler 自定捕获的异常类型。
3:通过 @ResponseBody 返回 json 到前端。
注意一点:被@ControllerAdvice注解的全局异常处理类也是一个 Controller ,我们需要配置扫描路径,确保能够扫描到这个Controller。
/**
* 全局Controller层异常处理类
*/
@ControllerAdvice
public class GlobalExceptionResolver {
/**
*处理所有不可知异常
* @param e 异常
* @return json结果
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public ApiResult handleException(Exception e) {
System.out.println(e.getMessage());
return ApiResult.of(ResultCode.UNKNOWN_ERROR);
}
/**
* 处理所有业务异常
* @param e 业务异常
* @return json结果
*/
@ExceptionHandler(BusinessRuntimeException.class)
@ResponseBody
public ApiResult handleOpdRuntimeException(BusinessRuntimeException e) {
System.out.println(e.getMessage());
return ApiResult.of(e.getResultCode());
}
}
二、测试
1、测试 TestExceptionController
@Controller
@RequestMapping("/test/")
public class TestExceptionController {
/**
* 测试返回异常信息
* @return
*/
@RequestMapping(value = "exception.do",method = RequestMethod.GET)
public String returnExceptionInfo() {
if (1 != 2) {
// 用户民错误或不存在异常
throw new BusinessRuntimeException(ResultCode.USERNAME_ERROR);
}
return "success";
}
}
效果:
其他比较全的文章可以看这个: SpringMVC中的统一异常处理
来源:oschina
链接:https://my.oschina.net/u/4401050/blog/3350840