其实Spring系列的项目全局异常处理方式早已存在,只不过我们一直忙于搬砖,很少停下脚步去审视这个日夜与我们相伴的朋友。为了贴合主题,本次主要针对SpringBoot全局异常处理进行举例说明。
SpringBoot中有一个@ControllerAdvice的注解,使用该注解即表示开启全局异常捕获,接下来我们只需在自定义的方法上使用@ExceptionHandler注解,并定义捕获异常的类型,对这种类型的异常进行统一的处理。
举个例子:
假如我们需要针对NullException(空指针异常,是Java程序员最痛恨的异常,没有之一)进行全局处理(如下所示)。
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理空指针的异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =NullPointerException.class)
public BaseResponseFacade exceptionHandler(HttpServletRequest req, NullPointerException e){
log.error("发生空指针异常!原因是:",e);
return ResponseUtil.error(ResponseCode.ERROR);
}
}
讷,就这么简单。其他可能发生的异常,都可以以这种方式处理快速处理。此处大家应该表现的十分兴奋,但请不要高兴太早,因为接下来,有更令激动人心的事情。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @Description 自定义异常
* @Date 2019-08-05 15:49
* @Created by 程序员小明
*/
@Data
@AllArgsConstructor
@Accessors(chain = true)
public class BizException extends RuntimeException {
/**
* 错误码
*/
protected Integer errorCode;
/**
* 错误信息
*/
protected String errorMsg;
}
显而易见,这个异常继承了RuntimeException,属于运行时异常。细心的朋友已经发现,我使用了Lombok插件,非常契合今天的主题,给大家简单介绍一下:
lombok是一个可以帮助我们简化java代码编写的工具类,尤其是简化javabean的编写,即通过采用注解的方式,消除代码中的构造方法,getter/setter等代码,使我们写的类更加简洁(如果使用的IDE是idea,需要安装插件哈)。
定义过之后,我们就可以和之前处理NullException方式一样处理我们自定义的异常。包括处理其他异常,都是这种方式。直接贴代码。
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义的业务异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = BizException.class)
public BaseResponseFacade bizExceptionHandler(HttpServletRequest req, BizException e){
log.error("发生业务异常!原因是:{}",e.getErrorMsg());
return ResponseUtil.error(e.getErrorCode(),e.getErrorMsg());
}
/**
* 处理空指针的异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =NullPointerException.class)
public BaseResponseFacade exceptionHandler(HttpServletRequest req, NullPointerException e){
log.error("发生空指针异常!原因是:",e);
return ResponseUtil.error(ResponseCode.ERROR);
}
/**
* 处理其他异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =Exception.class)
public BaseResponseFacade exceptionHandler(HttpServletRequest req, Exception e){
log.error("未知异常!原因是:",e);
return ResponseUtil.error(ResponseCode.INTERNAL_SERVER_ERROR);
}
}
整个全局异常处理方式核心就是以上介绍这些。下面用一个Demo给大家举个例子
@GetMapping("/test")
public BaseResponseFacade test(){
if(true){
throw new BizException(1,"error");
}
return ResponseUtil.success();
}
地址栏请求,进入这个方法后,会抛出异常,此时全局异常生效,就会返回异常处理过后的信息
{"errorCode":1,"errorMsg":"error","data":null}
到此整个流程都然跑通了。当然,好多地方都可以根据我们自身实际业务情况以此为基础进一步丰富,比如返回数据可以改成跳转某一个具体的页面。这样的出场方式是不是都很优雅?大家有什么问题,期待各位留言。
补充
/**
* @RestControllerAdvice = @ControllerAdvice + @ResponseBody
*/
堆栈信息的日志格式输出
for (StackTraceElement ele : e.getStackTrace()) {
sbException.append(MessageFormat.format("\tat {0}.{1}({2}:{3})\n",
ele.getClassName(), ele.getMethodName(), ele.getFileName(), ele.getLineNumber()));;
}
2、所有异常处理如果自定义的未捕获到,都会这Exception.class的异常
/**
* @RestControllerAdvice = @ControllerAdvice + @ResponseBody
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理自定义的业务异常,如果注释这里,就会走父类的异常捕获方法
* @param e
*/
// @ExceptionHandler(value = BizException.class)
// @ResponseBody
// public Result bizExceptionHandler(BizException e) {
// String message = e.getMessage();
// e.printStackTrace();
// logger.error(message);
logger.error("发生业务异常!原因是:{}", e.getMessage());
// return new Result(CommonEnum.ERROR,e.getMessage());
// }
/**
* 处理空指针的异常
* @param e
*/
@ExceptionHandler(value = NullPointerException.class)
@ResponseBody
public Result exceptionHandler(NullPointerException e) {
logger.error("发生空指针异常!原因是:", e);
return new Result(CommonEnum.BODY_NOT_MATCH,"空指针异常");
}
/**
* 方法安全权限异常
* @param e
*/
@ExceptionHandler(value = IllegalAccessException.class)
@ResponseBody
public Result exceptionHandler(IllegalAccessException e) {
logger.error("发生方法安全权限异常!原因是反射了private方法:", e);
return new Result(CommonEnum.INTERNAL_SERVER_ERROR,"方法安全权限异常!不能反射private方法");
}
/**
* 数学运算异常
* @param e
* @return
*/
@ExceptionHandler(value = ArithmeticException.class)
@ResponseBody
public Result exceptionHandler(ArithmeticException e) {
logger.error("发生数学运算异常:", e);
return new Result(CommonEnum.INTERNAL_SERVER_ERROR,"数学运算异常");
}
/**
* 处理其他异常
* @param e
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest request, Exception e) {
StackTraceElement[] stackTrace = e.getStackTrace();
logger.error("未知异常!原因是:", e);
return new Result(CommonEnum.INTERNAL_SERVER_ERROR,"未知异常!");
}
}