SpringBoot全局异常处理

1.前言

虽然SpringBoot已经提供了异常处理机制,但是这种方式不够灵活。可以根据其异常处理机制进行优化,自定义全局异常处理。

2.实战演练

2.1环境准备

SpringBoot版本:2.3.4.RELEASE

pom.xml需要的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.66</version>
</dependency>

2.2代码实现

1)首先定义一个基础的接口类,该接口由自定义的错误描述枚举类实现

/***
 * 错误信息的基类接口
 */
public interface BaseErrorInfoInterface {
    /**
     * 错误码
     */
    String getResultCode();

    /**
     * 错误描述
     */
    String getResultMsg();
}

2)自定义一个枚举类,枚举可能出现的异常信息

/**
 * 枚举类
 */
@AllArgsConstructor
public enum CommonEnum implements BaseErrorInfoInterface {

    // 数据操作错误定义
    SUCCESS("200", "成功!"),
    BODY_NOT_MATCH("400", "请求的数据格式不符!"),
    SIGNATURE_NOT_MATCH("401", "请求的数字签名不匹配!"),
    NOT_FOUND("404", "未找到该资源!"),
    INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),
    SERVER_BUSY("503", "服务器正忙,请稍后再试!");

    /**
     * 错误码
     */
    private String resultCode;

    /**
     * 错误描述
     */
    private String resultMsg;

    /**
     * 重写接口的方法,返回信息
     *
     * @return
     */
    @Override
    public String getResultCode() {
        return resultCode;
    }

    @Override
    public String getResultMsg() {
        return resultMsg;
    }
}

3)自定义一个异常类,用于处理在业务操作过程中可能出现的异常

/**
 * 自定义异常类
 */
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class MyException extends RuntimeException{

    /**
     * 错误码
     */
    protected String errorCode;
    /**
     * 错误信息
     */
    protected String errorMsg;


    public MyException(String errorMsg) {
        super(errorMsg);
        this.errorMsg = errorMsg;
    }

    public MyException(String errorCode, String errorMsg, Throwable cause) {
        super(errorCode, cause);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

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

4)为了数据统一格式返回,自定义一个数据的返回类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResultBody {
    /**
     * 响应代码
     */
    private String code;

    /**
     * 响应消息
     */
    private String message;

    /**
     * 响应结果
     */
    private Object result;

    public ResultBody(BaseErrorInfoInterface errorInfo) {
        this.code = errorInfo.getResultCode();
        this.message = errorInfo.getResultMsg();
    }

    /**
     * 成功
     *
     * @return
     */
    public static ResultBody success() {
        return success(null);
    }

    /**
     * 成功
     *
     * @param data
     * @return
     */
    public static ResultBody success(Object data) {
        ResultBody rb = new ResultBody();
        rb.setCode(CommonEnum.SUCCESS.getResultCode());
        rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
        rb.setResult(data);
        return rb;
    }

    /**
     * 失败
     */
    public static ResultBody error(BaseErrorInfoInterface errorInfo) {
        ResultBody rb = new ResultBody();
        rb.setCode(errorInfo.getResultCode());
        rb.setMessage(errorInfo.getResultMsg());
        rb.setResult(null);
        return rb;
    }

    /**
     * 失败
     */
    public static ResultBody error(String code, String message) {
        ResultBody rb = new ResultBody();
        rb.setCode(code);
        rb.setMessage(message);
        rb.setResult(null);
        return rb;
    }

    /**
     * 失败
     */
    public static ResultBody error(String message) {
        ResultBody rb = new ResultBody();
        rb.setCode("-1");
        rb.setMessage(message);
        rb.setResult(null);
        return rb;
    }

    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }
}

5)自定义全局异常处理类,用于捕获并处理所有的异常

@ControllerAdvice
@Slf4j
@RestController
public class GlobalExceptionHandler {

    /**
     * 处理自定义的业务异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = MyException.class)
    public ResultBody bizExceptionHandler(MyException e) {
        log.error("发生业务异常!原因是:{}", e.getErrorMsg());
        return ResultBody.error(e.getErrorCode(), e.getErrorMsg());
    }

    /**
     * 处理空指针的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = NullPointerException.class)
    public ResultBody exceptionHandler(NullPointerException e) {
        log.error("发生空指针异常!原因是:", e);
        return ResultBody.error(CommonEnum.BODY_NOT_MATCH);
    }

    /**
     * 处理其他异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public ResultBody exceptionHandler(Exception e) {
        log.error("未知异常!原因是:", e);
        return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);
    }
}

6)新建一个controller即可,用于测试

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/get")
    public String test() {
        int i = 10 / 0;
        return "hello";
    }

    @GetMapping("/get2")
    public String test2(String str) {
        return str.toLowerCase();
    }

    @PostMapping("add")
    public String test3(@RequestBody JSONObject json) {
        if (json.get("name") == null) {
            throw new MyException("-1", "姓名不能为空");
        }
        return json.get("name") + " ****";
    }
}

7)使用postman分别对三个接口进行访问

访问localhost:8080/test/get,返回结果如下图:

访问localhost:8080/test/get2,不传递参数,如下图:

访问localhost:8080/test/get2?str=123,如下图:

访问localhost:8080/test/add,name的值是null,如下图:

访问localhost:8080/test/add,name的值不为null,如下图: 

3.获取异常信息

一般情况下都是需要将异常信息记录下来,便宜运维,而若只使用e.getMessage()只能获取到异常的错误信息,却不知道异常发生在那里。下面通过两种不同的方法获取异常的完整信息

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 异常工具类
 */
public class ExceptionUtil {


    /**
     * 获取完整的异常信息
     *
     * @param ex
     * @return
     */
    public static String getAllExceptionInformation(Exception ex) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter writer = new PrintWriter(stringWriter);
        ex.printStackTrace(writer);
        StringBuffer buffer = stringWriter.getBuffer();
        return buffer.toString();
    }

    /**
     * 获取完整的异常信息
     *
     * @param ex
     * @return
     */
    public static String getAllExceptionInformation2(Exception ex) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream pout = new PrintStream(out);
        ex.printStackTrace(pout);
        String ret = new String(out.toByteArray());
        pout.close();
        try {
            out.close();
        } catch (Exception e) {
        }
        return ret;
    }

}

在业务中可直接调用,通过对比发现只是通过不同的方法将异常信息打印到流中。

posted @ 2021-04-13 19:13  钟小嘿  阅读(504)  评论(0编辑  收藏  举报