简介
项目中编写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;
}