简介
项目中编写API的时候因为要处理异常,所以代码中最常见的就是try-catch-finally,有时一个try,多个catch,代码既不美观,写的时候还很麻烦,Spring中提供了处理全局异常的方式,一个项目中只需要定义一次就不用在四处try-catch了,省时省力又优雅。
用法
Spring能够较好的处理这种问题,核心关注如下两个注解:
-
- @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
- @RestControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开,返回json (或使用@ControllerAdvice,与@Controller和@RestController的作用相似)
代码
全局异常处理类
定义一个全局异常处理类,对需要分开捕获对异常使用@ExceptionHandler分开捕获,单独处理,不用单独捕获的异常可以捕获Exception进行兜底处理,返回值可以返回项目中定义的统一返回格式,这里因为是代码示例,所以返回的都是String或者Void,@ExceptionHandler里面可以定义多个异常。定义好之后Controller中发生的异常都会到这里来统一处理
/** * @author LiuHuan * @date 2019-11-22 15:55 * @desc 全局异常处理类 */ @RestControllerAdvice public class GlobalExceptionHandler { /** * 全局处理Exception类型的异常 * @param e * @return */ @ExceptionHandler(Exception.class) public String processException(Exception e){ System.out.println("进入全局处理Exception方法"); System.out.println(getExceptionLocation(e)); e.printStackTrace(); System.out.println("结束全局处理Exception方法"); return "参数非法"; } /** * 全局处理NullPointerException类型的异常 * @param e * @return */ @ExceptionHandler(NullPointerException.class) public void processNullPointerException(NullPointerException e){ System.out.println("进入全局处理NullPointerException方法"); System.out.println(getExceptionLocation(e)); e.printStackTrace(); System.out.println("结束全局处理NullPointerException方法"); } /** * 全局处理IllegalArgumentException类型的异常 * @param e * @return */ @ExceptionHandler(IllegalArgumentException.class) public void processIllegalArgumentException(IllegalArgumentException e){ System.out.println("进入全局处理IllegalArgumentException方法"); System.out.println(getExceptionLocation(e)); e.printStackTrace(); System.out.println("结束全局处理IllegalArgumentException方法"); } /** * 全局处理RuntimeException类型的异常 * @param e * @return */ @ExceptionHandler(RuntimeException.class) public String processRuntimeException(RuntimeException e){ System.out.println("进入全局处理RuntimeException方法"); System.out.println(getExceptionLocation(e)); e.printStackTrace(); System.out.println("结束全局处理RuntimeException方法"); return "参数非法"; } /** * 获取异常发生的位置,可用于打印日志 * @param e * @return */ private String getExceptionLocation(Exception e){ StackTraceElement[] stackTrace = e.getStackTrace(); StackTraceElement stack = stackTrace[0]; String string = "%s.%s#%s.%s"; String format = String.format(string, stack.getClassName(), stack.getMethodName(), stack.getFileName(), stack.getLineNumber()); return format; } }
参数校验
结合sping的参数校验注解,对参数进行校验,然后按照项目中返回格式统一返回,避免在Controller中的try-catch捕获不到方法之前的参数校验异常
/** * 全局处理BindException类型的异常 * @param e * @return */ @ExceptionHandler({BindException.class}) public List<String> processBindException(BindException e) { System.out.println("进入全局处理BindException方法"); System.out.println(getExceptionLocation(e)); e.printStackTrace(); List<ObjectError> allErrors = e.getAllErrors(); List<String> error = allErrors. stream(). map(objectError -> ((FieldError)objectError).getField() + objectError.getDefaultMessage()).collect( Collectors.toList()); System.out.println("结束全局处理BindException方法"); return error; }
异常测试类
/** * @author LiuHuan * @date 2019-11-02 11:58 * @desc 异常测试类 */ @RestController public class GlobalExceptionTestController { @RequestMapping("/hello") public String hello(){ return "hello world"; } @RequestMapping("/processException") public void processException(@Validated @RequestBody User user){ System.out.println(user.toString()); } @RequestMapping("/processNullPointerException") public void processNullPointerException(){ throw new NullPointerException(); } @RequestMapping("/processIllegalArgumentException") public void processIllegalArgumentException(Exception e){ throw new IllegalArgumentException(); } @RequestMapping("/processRuntimeException") public void processRuntimeException(Exception e){ throw new RuntimeException(); } }
需要校验的入参类
/** * @author LiuHuan * @date 2019-11-22 16:44 * @desc 需要校验的入参 */ @Data public class User { @NotBlank private String name; @NotNull private Integer age; @NotEmpty private List<String> fruit; }