Spring boot2.x-第02讲:全局异常处理

本文详细介绍SpringBoot中全局异常处理的实现方法,包括自定义异常类、异常数据模型、异常枚举类及全局异常处理类的设计。通过具体示例展示如何在Controller中抛出异常,并由全局异常处理类统一捕获和处理。

1.前言

springboot中,默认在发送异常时,会跳转值/error请求进行错误的展现,根据不同的Content-Type展现不同的错误结果,如json请求时,直接返回json格式参数。
浏览器访问异常时:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu Sep 19 23:14:40 CST 2019
There was an unexpected error (type=Not Found, status=404).
No message available

2.全局统一异常处理

利用@ControllerAdvice和@ExceptionHandler定义一个统一异常处理类。

  • @ControllerAdvice:控制器增强,被@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法,都会作用在被 @RequestMapping注解的方法上。

  • @ExceptionHandler:异常处理器,此注解的作用是当出现其定义的异常时进行处理的方法。该注解拦截异常,我们可以通过该注解实现自定义异常处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型

2.1自定义异常类

代码如下:

/**
* 平台级异常基类(存储异常码和异常消息)
**/
@Setter
@Getter
public class TongPlatformException extends RuntimeException {

    private static final long serialVersionUID = -8252342484961827798L;

    private String exceptionCode;

    private String exceptionMessage;

    public TongPlatformException(String exceptionCode, String exceptionMessage) {
        // 在打印堆栈信息时会打印出来,示例:[{"exceptionCode":"123456", "exceptionMessage":"异常测试"}]
        super("[{\"exceptionCode\" : \"".concat(exceptionCode).concat("\", \"exceptionMessage\" : \"").concat(exceptionMessage).concat("\"}]"));
        this.exceptionCode = exceptionCode;
        this.exceptionMessage = exceptionMessage;
    }
}

【注意】:Spring 对于 RuntimeException类的异常才会进行事务回滚,所以我们一般自定义异常都继承该异常类。

/**
* 业务异常类
**/
public class BusinessException extends TongPlatformException {

    private static final long serialVersionUID = 5964974489633380622L;

    public BusinessException(String exceptionCode, String exceptionMessage) {
        super(exceptionCode, exceptionMessage);
    }
}

2.2异常数据模型

该模型的作用:全局异常处理类中需要使用该模型存储错误码和错误消息,然后返回给调用系统or前端页面。

@Setter
@Getter
public class ExceptionResult implements Serializable {

    private static final long serialVersionUID = -7393066081035082131L;

    private String retCode;
    private String retMsg;

    private ExceptionResult(String retCode, String retMsg) {
        this.retCode = retCode;
        this.retMsg = retMsg;
    }

    public static ExceptionResult exception(String retCode, String retMsg) {
        return new ExceptionResult(retCode, retMsg);
    }
}

2.3异常枚举类

public enum ExceptionEnum {

    FAILED("999999", "业务执行异常!");

    //自定义异常码
    private String code;
    //异常信息说明
    private String message;

    ExceptionEnum(String code, String message) {
        this.code = code;
        this.message = message;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

}

【注意】:该枚举类没有setter方法。原因:尽量不要让其他类改变预先规划好的错误码和错误消息。

2.4全局异常处理类

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * ExceptionHandler拦截了异常,我们可以通过该注解实现自定义异常处理。
     * 其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型。
     */
    @ExceptionHandler(value = {Exception.class})
//    @ResponseBody
    public ExceptionResult handlerException(Exception ex) {
        log.info("com.tong.hao.online.service.handler.GlobalExceptionHandler.handlerException");
        log.info(ex.getMessage(), ex);
        if (ex instanceof BusinessException) {
            BusinessException businessException = (BusinessException) ex;
            return ExceptionResult.exception(businessException.getExceptionCode(), businessException.getExceptionMessage());
        }
        return ExceptionResult.exception(ExceptionEnum.FAILED.getCode(), ExceptionEnum.FAILED.getMessage());
    }

}

【注意】:这里我使用的是@RestControllerAdvice,@RestControllerAdvice是一个组合注解,组合了@ControllerAdvice、@ResponseBody,因为我这里是演示直接返回的是内容,所以为了方便使用@RestControllerAdvice,如果你们的异常需要返回页面啊之类的,你可以使用@ControllerAdvice分别定制。

2.5Controller

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

    @ApiOperation(value = "业务异常测试")
    @RequestMapping(value = "/exception")
    public String testException() {
        StringBuilder strBuilder = new StringBuilder("abc");
        try {
            int a = Integer.valueOf(strBuilder.toString());
        } catch (NumberFormatException e) {
            // 此处抛出业务异常,会被GlobalExceptionHandler中的ExceptionHandler拦截进行后续处理
            throw BusinessUtils.createBusinessException("123456", "异常测试");
        }
        return strBuilder.toString();
    }
    
}

2.6测试

在浏览器地址栏输入 http://127.0.0.1:9090/tong/test/exception
页面返回如下:

{“retCode”:“123456”,“retMsg”:“异常测试”}

2.7执行流程

本人见解:

  • 浏览器访问/test/exception;
  • TestController.testException()方法中抛出自定义业务异常BusinessException;
  • 然后被全局异常处理类GlobalExceptionHandler拦截;
  • 再然后返回ExceptionResult(存储了自定义异常的错误码和错误消息);
  • 由于GlobalExceptionHandler使用的controller增强器使用的是@RestControllerAdvice注解,所以,最终返回的是json格式的数据。

参考:
SpringBoot | 第八章:统一异常、数据校验处理
https://blog.lqdev.cn/2018/07/20/springboot/chapter-eight/

Spring Boot 系列 - @ControllerAdvice & 拦截异常并统一处理
https://my.oschina.net/magicalSam/blog/1456337

9.玩转Spring Boot 全局异常处理@ControllerAdvice
https://blog.youkuaiyun.com/cl_andywin/article/details/53790510

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值