首页 > 其他分享 >SpringBoot实现统一异常处理

SpringBoot实现统一异常处理

时间:2024-02-05 09:01:02浏览次数:26  
标签:String ResponseResult message 统一 异常 public BizException SpringBoot

目录

image

前言

近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功能开发。本项目为前后端分离开发,后端基于Java21SpringBoot3开发,后端使用Spring SecurityJWTSpring Data JPA等技术栈,前端提供了vueangularreactuniapp微信小程序等多种脚手架工程。

项目地址:https://gitee.com/breezefaith/fast-alden

在前后端分离的项目开发过程中,我们通常会对数据返回格式进行统一的处理,这样可以方便前端人员取数据,后端发生异常时同样会使用此格式将异常信息返回给前端。本文将介绍在SpringBoot项目中如何实现统一异常处理。

实现步骤

定义统一响应对象类

/**
 * 响应结果类
 *
 * @param <T> 任意类型
 */
@Data
public class ResponseResult<T> {
    /**
     * 响应状态码,200是正常,非200表示异常
     */
    private int status;
    /**
     * 异常编号
     */
    private String errorCode;
    /**
     * 异常信息
     */
    private String message;
    /**
     * 响应数据
     */
    private T data;

    public static <T> ResponseResult<T> success() {
        return success(HttpServletResponse.SC_OK, null, null);
    }

    public static <T> ResponseResult<T> success(T data) {
        return success(HttpServletResponse.SC_OK, null, data);
    }

    public static <T> ResponseResult<T> fail(String message) {
        return fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, message, null);
    }

    public static <T> ResponseResult<T> fail(String errorCode, String message) {
        return fail(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorCode, message, null);
    }

    public static <T> ResponseResult<T> success(int status, String message, T data) {
        ResponseResult<T> r = new ResponseResult<>();
        r.setStatus(status);
        r.setMessage(message);
        r.setData(data);

        return r;
    }

    public static <T> ResponseResult<T> fail(int status, String errorCode, String message) {
        return fail(status, errorCode, message, null);
    }

    public static <T> ResponseResult<T> fail(int status, String errorCode, String message, T data) {
        ResponseResult<T> r = new ResponseResult<>();
        r.setStatus(status);
        r.setErrorCode(errorCode);
        r.setMessage(message);
        r.setData(data);
        return r;
    }

}

定义业务异常枚举接口和实现

通常一个系统中的自定义业务异常是可穷举的,可以考虑通过定义枚举的方式来列举所有的业务异常。

首先我们来定义一个异常信息枚举的基类接口。

public interface IBizExceptionEnum {
    String getCode();

    String getMessage();
}

再给出一个常用的异常信息的枚举类,如果有其他业务模块的异常信息,同样可以通过实现IBizExceptionEnum接口来进行定义。

@Getter
public enum BizExceptionEnum implements IBizExceptionEnum {
    ENTITY_IS_NULL("Base_Entity_Exception_0001", "实体为空"),
    ENTITY_ID_IS_NULL("Base_Entity_Exception_0002", "实体id字段为空"),
    ENTITY_ID_IS_DUPLCATED("Base_Entity_Exception_0003", "实体id字段%s重复");

    private final String code;
    private final String message;

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

定义业务异常基类

业务异常基类BizException继承自RuntimeException,代码中主动抛出的异常建议都包装为该类的实例。

/**
 * 业务异常基类,支持参数化的异常信息
 */
@Getter
@Setter
public class BizException extends RuntimeException {
    private String code;
    private Object[] args;

    public BizException() {
        super();
    }

    public BizException(String message) {
        super(message);
    }

    public BizException(Throwable cause) {
        super(cause);
    }

    public BizException(String message, Throwable cause) {
        super(message, cause);
    }

    public BizException(Throwable cause, String code, String message, Object... args) {
        super(message, cause);
        this.code = code;
        this.args = args;
    }

    public BizException(String code, String message, Object... args) {
        super(message);
        this.code = code;
        this.args = args;
    }

    public BizException(IBizExceptionEnum exceptionEnum, Object... args) {
        this(exceptionEnum.getCode(), exceptionEnum.getMessage(), args);
    }

    public BizException(Throwable cause, IBizExceptionEnum exceptionEnum, Object... args) {
        this(cause, exceptionEnum.getCode(), exceptionEnum.getMessage(), args);
    }

    @Override
    public String getMessage() {
        if (code != null) {
            if (args != null && args.length > 0) {
                return String.format(super.getMessage(), args);
            }
        }
        return super.getMessage();
    }
}

定义全局异常处理切面

本步骤需要使用@RestControllerAdvice注解,它是一个组合注解,由@ControllerAdvice@ResponseBody组成,而@ControllerAdvice继承了@Component,因此@RestControllerAdvice本质上是个Component,用于定义@ExceptionHandler@InitBinder@ModelAttribute方法,适用于所有使用@RequestMapping方法。

还要用到@ExceptionHandler注解,可以认为它是一个异常拦截器,它采用“就近原则”,存在多个满足条件的异常处理器时会选择最接近的一个来使用。它本质上就是使用Spring AOP定义的一个切面,在系统抛出异常后执行。

具体实现代码如下:

/**
 * 全局异常处理切面
 */
@RestControllerAdvice
public class GlobalExceptionHandlerAdvice {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandlerAdvice.class);

    @ExceptionHandler({BizException.class})
    public ResponseResult<Object> handleBizException(BizException e, HttpServletRequest request, HttpServletResponse response) {
        log.error(e.getCode() + ": " + e.getMessage(), e);
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        return ResponseResult.fail(e.getCode(), e.getMessage());
    }

    @ExceptionHandler({RuntimeException.class, Exception.class})
    public ResponseResult<Object> handleRuntimeException(Exception e, HttpServletRequest request, HttpServletResponse response) {
        log.error(e.getMessage(), e);
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        return ResponseResult.fail(e.getMessage());
    }
}

上述代码会对系统中抛出的BizExceptionRuntimeExceptionException对象进行处理,把异常包装为ResponseResult对象后将异常编号和异常信息返回给前端。

测试和验证

下面我们就可以定义一个Controller类来进行简单的测试。

@RestController
@RequestMapping("/demo")
public class DemoController {
    @GetMapping("/method1")
    public ResponseResult<Integer> method1() {
        throw new BizException(BizExceptionEnum.ENTITY_IS_NULL);
    }

    @GetMapping("/method2")
    public void method2() {
        throw new BizException(BizExceptionEnum.ENTITY_ID_IS_NULL);
    }

    @GetMapping(value = "/method3")
    public String method3() {
        throw new BizException(BizExceptionEnum.ENTITY_ID_IS_DUPLCATED, "1");
    }

    @GetMapping(value = "/method4")
    public String method4() {
        // 抛出ArithmeticException异常
        return String.valueOf(1 / 0);
    }
}

总结

本文介绍了如何在SpringBoot项目中实现统一异常处理,如有错误,还望批评指正。

在后续实践中我也是及时更新自己的学习心得和经验总结,希望与诸位看官一起进步。

标签:String,ResponseResult,message,统一,异常,public,BizException,SpringBoot
From: https://www.cnblogs.com/breezefaith/p/18006315

相关文章

  • 全网最全:SpringBoot 各种回滚骚操作实战
    事务定义事务,就是一组操作数据库的动作集合。事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其中的一部分或多步执行失败,导致没有步骤被提交......
  • 交换机ARP学习异常,看网工大佬是如何处理的?
    晚上好,我的网工朋友。在复杂多变的网络环境中,网工就没有不遇到问题的,习惯了,兵来将挡水来土掩。那ARP异常你遇到过吗?ARP作为网络中的基础协议之一,它的稳定性对整个网络的性能和可靠性至关重要。比如说,交换机上遇到了大量的不完整ARP表项导致用户终端的ARP缓存不稳定,这个情况,怎么自查......
  • 开发小技巧 - springboot项目启动控制台输出访问地址
    在SpringBoot项目中,有时我们需要记录或输出访问的地址和IP,以便进行调试、监控或日志记录。以下是如何在SpringBoot中实现这一需求的方法: 1、编写获取所有访问地址工具类packagecom.example.utils;importcn.hutool.core.util.StrUtil;importorg.springframework.b......
  • SpringBoot连接MySQL
    一、文件结构: 二、实体类packagecom.example.demo.domain;importjava.io.Serializable;importjavax.persistence.Column;importjavax.persistence.Entity;importjavax.persistence.GeneratedValue;importjavax.persistence.Id;importjavax.persistence.Table;......
  • SpringBoot 整合 RabbitMQ
    Docker搭建RabbitMQ拉取RabbitMQ的镜像执行命令dockerpullrabbitmq:3.7-management执行运行命令dockerrun-d--hostnamerabbit--namerabbit-eRABBITMQ_DEFAULT_USER=admin-eRABBITMQ_DEFAULT_PASS=admin-p15672:15672rabbitmq:3-management打开浏览器访问......
  • Springboot项目发布war到tomcat
    springboot项目有些日子没有开发了,新做一个minspringboot项目,复习下项目开发及发布流程。1.新建项目: 2.新建一个业务controllercontroller名称及方法,名称随意,项目结构如下: testcontroller代码文件的内容如下:packagecom.*****.Controller;importorg.springframewor......
  • SpringBoot-热部署插件添加
      在开发中修改代码避免反复重启编译   <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency> 使用idea为2023.2.3 ......
  • SpringBoot的约定是什么?
    springboot项目中必须在src/main/resources中放入application.yml(yaml,properties)配置文件,名字为applicationspringboot项目中必须在src/main/java中只能有一个启动类......
  • apache如何配置统一请求随机转发到不同服务
    ApacheHTTP服务器可以使用mod_proxy和mod_proxy_balancer模块来配置统一请求随机转发到不同的后端服务。这里提供一个基本的配置示例,展示如何实现随机负载均衡。首先,确保你的Apache服务器已经安装了mod_proxy和mod_proxy_balancer模块。在大多数Linux发行版中,这些模块默认是安装......
  • SpringBoot简单集成JWT
    1.JWT入门1.1JWT概念官方网站:https://jwt.io/introduction/JSONWebToken(JWT)是一个定义在RFC7519开放标准下的技术,提供了一种紧凑且自包含的方式用于在各方之间安全地传输信息。JWT使用JSON对象作为载体,同时通过数字签名来验证和确保信息的可信度。数字签名可以通过......