原创

在 springboot 项目中全局处理异常

作者:残城碎梦 围观群众:569 更新于 标签:spring boot异常处理全局异常处理

前言

相信大家在编写代码的时候都很烦恼一件事。那就是频繁的异常处理。大量的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。


最后欢迎大家访问我的个人博客网站:zShare个人博客