原创 2021-05-06 10:24:03

前言


相信大家在编写代码的时候都很烦恼一件事。那就是频繁的异常处理。大量的try catch在逻辑层中使用不仅非常麻烦。也让我们的代码可读性较差。所以在spring boot 项目中使用全局异常处理是非常有必要的。

 

注解解析


@ControllerAdvice注解
在spring中可以使用@ControllerAdvice 声明一些全局的东西。例如全局异常处理,数据绑定,数据异常处理等。在这里我们需要与@ExceptionHandler来结合使用做全局异常处理。
@ExceptionHandler注解
使用@ExceptionHandler注解可以统一的对某类异常进行处理。

 

代码实现


在对@ExceptionHandler和@ControllerAdvice注解有了一定的了解以后。我们开始我们的代码实现。

1.定义基本异常接口
 

package top.demo.common.exception;

/**
*
* @author zzy
* @date 2021/3/15 15:42
*/
public interface BaseExceptionInfo {

   /**
    * 获取错误码
    * @return
    */
   String getErrCode();

   /**
    * 获取错误描述
    * @return
    */
   String getErrMsg();
}


2.定义常见的异常枚举类

 

package top.demo.common.exception;

/**
* 异常枚举类
*
* @author zzy
* @date 2021/3/15 15:49
*/
public enum ErrCommonEnum implements BaseExceptionInfo {

   SUCCESS("200", "成功!"),
   BODY_NOT_MATCH("400", "请求的数据格式不符!"),
   NOT_LOGIN("401", "请登录后重新访问!"),
   NO_PERMISSION("402", "您没有权限访问!"),
   NOT_FOUND("404", "未找到该资源!"),
   INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
   SERVER_BUSY("503", "服务器正忙,请稍后再试!"),
   ;

   private String errCode; //错误码
   private String errMsg; //错误描述

   ErrCommonEnum(String errCode, String errMsg) {
       this.errCode = errCode;
       this.errMsg = errMsg;
   }


   @Override
   public String getErrCode() {
       return errCode;
   }

   @Override
   public String getErrMsg() {
       return errMsg;
   }
}


3.自定义异常

 

package top.demo.common.exception;

/**
* 自定义异常,处理业务异常
* @author zzy
* @date 2021/3/15 15:58
*/
public class CustomException extends RuntimeException {

   protected String errCode; //错误码

   protected String errMsg; //错误描述

   public CustomException(){
       super();
   }

   public CustomException(BaseExceptionInfo baseExceptionInfo){
       super(baseExceptionInfo.getErrCode());
       this.errCode = baseExceptionInfo.getErrCode();
       this.errMsg = baseExceptionInfo.getErrMsg();
   }

   public CustomException(BaseExceptionInfo baseExceptionInfo, Throwable cause){
       super(baseExceptionInfo.getErrCode(), cause);
       this.errCode = baseExceptionInfo.getErrCode();
       this.errMsg = baseExceptionInfo.getErrMsg();
   }

   public CustomException(String errMsg){
       super(errMsg);
       this.errMsg = errMsg;
   }

   public CustomException(String errCode, String errMsg){
       super(errCode);
       this.errCode = errCode;
       this.errMsg = errMsg;
   }

   public CustomException(String errCode, String errMsg, Throwable cause){
       super(errCode, cause);
       this.errCode = errCode;
       this.errMsg = errMsg;
   }

   public String getErrCode() {
       return errCode;
   }

   public void setErrCode(String errCode) {
       this.errCode = errCode;
   }

   public String getErrMsg() {
       return errMsg;
   }

   public void setErrMsg(String errMsg) {
       this.errMsg = errMsg;
   }

   @Override
   public Throwable fillInStackTrace() {
       return this;
   }
}


4.编写公共的api相应类。

 

package top.demo.common;

import top.demo.common.exception.ErrCommonEnum;

/**
* api接口响应类
* @author zzy
* @date 2021/3/15 16:14
*/
public class ApiResponse {

   private boolean success; //是否成功
   private String code; //状态码
   private String msg; //提示信息
   private Object data; //绑定的数据
   private Integer count; //如果是分页的话。返回数据总条数


   ApiResponse(boolean success, String code, String msg){
       this.success = success;
       this.code = code;
       this.msg = msg;
       this.data = null;
       this.count = 0;
   }

   ApiResponse(boolean success, String code, String msg, Object data){
       this.success = success;
       this.code = code;
       this.msg = msg;
       this.data = data;
       this.count = 0;
   }

   ApiResponse(boolean success, String code, String msg, Object data, Integer count){
       this.success = success;
       this.code = code;
       this.msg = msg;
       this.data = data;
       this.count = count;
   }

   public static ApiResponse ok(){
       return new ApiResponse(true,ErrCommonEnum.SUCCESS.getErrCode(),ErrCommonEnum.SUCCESS.getErrMsg());
   }

   public static ApiResponse ok(String msg){
       return ok(msg,null);
   }

   public static ApiResponse ok(String msg, Object data){
       return new ApiResponse(true, ErrCommonEnum.SUCCESS.getErrCode(),msg,data);
   }

   public static ApiResponse err(ErrCommonEnum errCommonEnum){
       return new ApiResponse(false,errCommonEnum.getErrCode(),errCommonEnum.getErrMsg());
   }

   public static ApiResponse err(String msg){
       return err("-1",msg);
   }

   public static ApiResponse err(String code ,String msg){
       return new ApiResponse(false,code,msg);
   }

   public static ApiResponse tableData(Object data,Integer count){
       return new ApiResponse(true,"200","获取成功", data, count);
   }

   public boolean isSuccess() {
       return success;
   }

   public void setSuccess(boolean success) {
       this.success = success;
   }

   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 Object getData() {
       return data;
   }

   public void setData(Object data) {
       this.data = data;
   }

   public Integer getCount() {
       return count;
   }

   public void setCount(Integer count) {
       this.count = count;
   }


   public void demo(){
       System.out.println();
   }
}


5.使用上面两个注解完成全局的异常处理

 

package top.demo.common.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import top.demo.common.ApiResponse;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
* 全局的异常处理类
* @author zzy
* @date 2021/3/15 18:50
*/
@ControllerAdvice
public class GlobalExceptionHandler {
  private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

   /**
    * 处理业务逻辑异常
    * @param req
    * @param e
    * @return
    */
   @ExceptionHandler(value = CustomException.class)
   @ResponseBody
   public ApiResponse customExceptionHandler(HttpServletRequest req,CustomException e){
       logger.error("发生业务逻辑异常! 原因是:{}",e.getErrMsg());
       return ApiResponse.err(e.getErrCode(),e.getErrCode());
   }

   /**
    * 捕获空指针异常
    * @param req
    * @param e
    * @return
    */
   @ExceptionHandler(value = NullPointerException.class)
   @ResponseBody
   public ApiResponse nullPointerExceptionHandler(HttpServletRequest req,NullPointerException e){
       e.printStackTrace();
       logger.error("发生了空指针异常! 原因是:{}",e.getMessage());
       return ApiResponse.err(ErrCommonEnum.INTERNAL_SERVER_ERROR);
   }

   /**
    * 捕获其他异常
    * @param req
    * @param e
    * @return
    */
   @ExceptionHandler(value = Exception.class)
   @ResponseBody
   public ApiResponse exceptionHandler(HttpServletRequest req,Exception e){
       e.printStackTrace();
       logger.error("未知异常! 原因是:{}",e.getMessage());
       return ApiResponse.err(ErrCommonEnum.INTERNAL_SERVER_ERROR);
   }

   @ExceptionHandler(value = Throwable.class)
   @ResponseBody
   public ApiResponse throwableHandler(HttpServletRequest req,Throwable e){
       logger.error("未知异常! 原因是:{}",e.getMessage());
       return ApiResponse.err(ErrCommonEnum.INTERNAL_SERVER_ERROR);
   }

}


测试与结果
 

package top.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import top.demo.common.ApiResponse;
import java.util.List;

/**
* @author zzy
* @date 2021/5/6 9:01
*/
@Controller
@RequestMapping("/demo")
public class DemoController {

   @GetMapping("/null")
   @ResponseBody
   public ApiResponse nullDemo() {
       List list = null;
       System.out.println(list.size());
       return ApiResponse.ok("空指针异常没有被捕获");
   }

   @GetMapping("/zero")
   @ResponseBody
   public ApiResponse zero(){
       int a = 1 / 0;
       System.out.println(a);
       return ApiResponse.ok("异常没有被捕获");
   }

   @GetMapping("/demo")
   @ResponseBody
   public ApiResponse demo(){
       return ApiResponse.ok("正常情况没有问题");
   }
}

 

全局异常处理捕获空指针异常
全局异常处理不过其他异常

 

正常的情况,不会被捕获

 

注意


在项目中使用一开始是没有任何问题的。但是给代码添加了切面日志处理之后发现全局的异常处理失效了。在检查代码发现。切面日志处理中含有try catch 。影响了全局异常处理。需要注意的地方是在逻辑代码中如果含有 try catch 会优先被try catch捕获。不会被全局异常处理捕获,其中包含切面中的try catch。
 

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

延伸阅读
  1. java基础模块面试题
  2. 一款比较有意思的404页面
  3. springboot static 代码块读取application.properties配置文件属性
  4. 10个艰难的Java面试题与答案
  5. 【Stackoverflow好问题】去掉烦人的“!=null"(判空语句)
发表评论