SpringBoot的全局异常拦截实践过程

2025-12-12 19:50

本文主要是介绍SpringBoot的全局异常拦截实践过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringBoot的全局异常拦截实践过程》SpringBoot中使用@ControllerAdvice和@ExceptionHandler实现全局异常拦截,@RestControllerAdvic...

在 Spring Boot 中,可以通过使用 @ControllerAdvice 注解和 @ExceptionHandler 注解来实现全局异常拦截。

@RestControllerAdvice

@RestControllerAdvice 是 Spring Framework 提供的注解,用于定义全局异常处理类,并且结合 @ExceptionHandler 注解来处理异常。与 @ControllerAdvice 不同的是,@RestControllerAdvice 默认情况下会将返回值转换为 jsON 格式。

@RestControllerAdvice
public class GlobalExceptionHandler {
    
  //.....拦截异常方法

}

@ResponseStatus(...)

@ResponseStatus(HttpStatus.BAD_REQUEST) 是一个注解,用于在异常处理方法上指定特定的HTTP状态码。当该异常被抛出时,将返回指定的HTTP状态码给客户端。

@RestControllerAdvice
public class GlobalExceptionHandler {
    
  //.....拦截异常方法
  /**
     * 缺少请求体异常处理器
     * @param e 缺少请求体异常 使用get方式请求 而实体使用@RequestBody修饰
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
        String requestURI = httpServletRequest.getRequestURI();
        log.error("请求地址'{}',请求体缺失'{}'", requestURI, e.getMessage());
        return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
    }
}

@ExceptionHandler(...)

@ExceptionHandler(...) 是一个异常处理注解,用于捕获请求的异常。当客户端发送的请求消息无法被框架正确解析时,将抛出该异常并调用对应的异常处理方法。

@RestControllerAdvice
public class GlobalExceptionHandler {
    
  //.....拦截异常方法
  /**
     * 缺少请求体异常处理器
     * @param e 缺少请求体异常 使用get方式请求 而实体使用@RequestBody修饰
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
        String requestURI = httpServletRequest.getRequestURI();
        log.error("请求地址'{}',请求体缺失'{}'", requestURI, e.getMessage(NzLaOsJoF));
        return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
    }
}

RuntimeException

RuntimeExceptionJava 提供的一个运行时异常类。与受检查异常不同,运行时异常不需要在方法声明中显式地声明或捕获,并且在运行时抛出时也不需要强制捕获或处理。所以我们可以在全局异常捕获中去捕获这个异常

public class BusinessException extends RuntimeException {

    private int code;
    //使用枚举构造
    public BusinessException(HttpCodeEnum httpCodeEnum){
        super(httpCodeEnum.getMsg());
        this.code=httpCodeEnum.getCode();
    }
    //使用自定义消息体
    public BusinessException(HttpCodeEnum httpCodeEnum, String msg){
        super(msg);
        this.code=httpCodeEnum.getCode();
    }

    //根据异常构造
    public BusinessException(HttpCodeEnum httpCodeEnum, Throwable msg){
        super(msg);
        this.code=httpCodeEnum.getCode();
    }


}

 上述代码定义了一个名为 BusinessException 的自定义异常类,它继承自 RuntimeException

这个自定义异常类具有以下特点:

  1. 包含了一个 code 字段,用于表示异常的错误码。
  2. 提供了不同的构造方法,以方便在抛出异常时指定错误码和错误信息。
  • BusinessException(HttpCodeEnum httpCodeEnum) 构造方法使用枚举类型 HttpCodeEnum 来设置异常的错误码和错误信息。
  • BusinessException(HttpCodeEnum httpCodeEnum, String msg) 构造方法使用自定义的错误信息来设置异常的错误码和错误信息。
  • BusinessException(HttpCodeEnum httpCodeEnum, Throwable msg) 构造方法使用其他异常的实例来设置异常的错误码,并可选地提供通过 Throwable 获取的错误信息。

HttpCodeEnum枚举类

我们还需要一个类表示 HTTP 响应的状态码和对应的消息 ,以下为基本的举例查考。

public enum HttpCodeEnum {
    // 成功
    SUCCESS(200, "操作成功"),
    // 登录
    NEED_LOGIN(401, "需要登录后操作"),
    NO_OPERATOR_AUTH(403, "无权限操作"),
    SYSTEM_ERROR(500, "出现错误"),
    USERNAME_EXIST(501, "用户名已存在"),
    PHONENUMBER_EXIST(502, "手机号已存在"), EMAIL_EXIST(503, "邮箱已存在"),
    REQUIRE_USERNAME(504, "必需填写用户名")China编程,
    CONTENT_NOT_NULL(506, "评论内容不能为空"),
    FILE_TYPE_ERROR(507, "文件类型错误"),
    USERNAME_NOT_NULL(508, "用户名不能为空"),
    NICKNAME_NOT_NULL(509, "昵称不能为空"),
    PASSWORD_NOT_NULL(510, "密码不能为空"),
    EMAIL_NOT_NULL(511, "邮箱不能为空"),
    NICKNAME_EXIST(512, "昵称已存在"),
    LOGIN_ERROR(505, "用户名或密码错误");
    int code;
    String msg;

    HttpCodeEnum(int code, String errorMessage) {
        this.code = code;
        this.msg = errorMessage;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

上述代码定义了一个 HttpCodeEnum 枚举类,用于表示 HTTP 响应的状态码和对应的消息。

这个枚举类具有以下特点:

  1. 包含一组枚举常量,每个常量代表一个 HTTP 响应状态。
  2. 每个常量都有一个整型的 code 和一个字符串类型的 msg,分别表示状态码和对应的消息。
  3. 提供了相应的构造方法、获取 code 和 msg 的方法。

ResponseResult类

该类的主要作用是封装接口返回的数据,统一格式化输出,方便前端调用和展示。

import lombok.Data;

import java.io.Serializable;
@Data
public class ResponseResult<T> implements Serializable {
    private Boolean success;
    private Integer code;
    private String msg;
    private T data;

    public ResponseResult() {
        this.success=true;
        this.code = HttpCodeEnum.SUCCESS.getCode();
        this.msg = HttpCodeEnum.SUCCESS.getMsg();
    }

    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public static ResponseResult errorResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.error(code, msg);
    }

    public static ResponseResult okResult() {
        ResponseResult result = new ResponseResult();
        return result;
    }

    public static ResponseResult okResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.ok(code, null, msg);
    }




    public static ResponseResult setHttpCodeEnum(HttpCodeEnum enums) {
        return okResult(enums.getCode(), enums.getMsg());
    }


    public ResponseResult<?> error(Integer code, String msg) {
        this.success=false;
        this.code = code;
        this.msg = msg;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data) {
        this.success=true;
        this.code = code;
        this.data = data;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data, String msg) {
        this.success=true;
        this.code = code;
        this.data = data;
        this.msg = msg;
        return this;
    }

    public ResponseResult<?> ok(T data) {
        this.success=true;
        this.data = data;
        return this;
    }


}

全局异常捕获

全局异常捕获是一种处理应用程序中未处理的异常的机制,它可以统一处理应用程序中的异常,避免异常导致程序崩溃或向用户显示不友好的错误信息。我们可以通过上述的解释去捕获异常,定义code类型枚举返回ResponseResult给前端

import com.example.demo.util.HttpCodeEnum;
import com.example.demo.util.ResponseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @Autowired
    private HttpServletRequest httpServletRequest;

    private final String sysError="系统出错";

    /**
     * 缺少请求体异常处理器
     * @param e 缺少请求体异常 使用getjs方式请求 而实体使用@RequestBody修饰
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseResult parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e) {
        String requestURI = httpServletRequest.getRequestURI();
        log.error("请求地址'{}',请求体缺失'{}'", requestURI, e.getMessage());
        return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
    }

    /*
     * @Description:  捕获请求方法异常,比如post接口使用了get
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseResult methodNotAllowedHandler(HttpRequestMethodNotSupportedException e) {
        String requestURI = httpServletRequest.getRequestURI();
        log.error("请求地址'{}',请求方法不被允许'{}'", requestURI, e.getMessage());
        return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
    }

    // get请求的对象参数校验异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public ResponseResult bindExceptionHandler(MissingServletRequestParameterException e) {
            String requestURI = httpServletRequest.getRequestURI();
        log.error("请求地址'{}',get方式请求参数'{}'必传", requestURI, e.getParameterName());
            return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), sysError);
    }
    // post请求的对象参数校验异常
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResponseResult methodArgumentNotValidHandler(MethodArgumentNotValidException e) {
        String requestURI = httpServletRequest.getRequestURI();
        log.error("请求地址'{}',post方式请求参数异常'{}'", requestURI, e.getMessage());
            List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
            return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), getValidExceptioChina编程nMsg(allErrors));
    }

    // 业务类异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BusinessException.class)
    public ResponseResult businessExceptionHandler(BusinessException e) {
        String requestURI = httpServletRequest.getRequestURI();
        System.out.println(e);
        log.error("请求地址'{}',捕获业务类异常'{}'", requestURI,e.getMessage());
        return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage());
    }
    // 运行时异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(RuntimeException.class)
    public ResponseResult runtimeExceptionHandler(RuntimeException e) {
        String requestURI = httpServletRequest.getRequestURI();
        lojavascriptg.error("请求地址'{}',捕获运行时异常'{}'", requestURI, e.getMessage());
        return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage());
    }
    // 系统级别异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Throwable.class)
    public ResponseResult throwableExceptionHandler(Throwable e) {
        String requestURI = httpServletRequest.getRequestURI();
        log.error("请求地址'{}',捕获系统级别异常'{}'", requestURI,e.getMessage());
        return ResponseResult.errorResult(HttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage());
    }



    private String getValidExceptionMsg(List<ObjectError> errors) {
        if(!CollectionUtils.isEmpty(errors)){
            StringBuilder sb = new StringBuilder();
            errors.forEach(error -> {
                if (error instanceof FieldError) {
                    sb.append(((FieldError)error).getField()).append(":");
                }
                sb.append(error.getDefaultMessage()).append(";");
            });
            String msg = sb.toString();
            msg = msg.substring(0, msg.length() -1);
            return msg;
        }
        return null;
    }

}

测试

入参不正确时

SpringBoot的全局异常拦截实践过程

发出请求

SpringBoot的全局异常拦截实践过程

返回结果

SpringBoot的全局异常拦截实践过程

捕获异常

SpringBoot的全局异常拦截实践过程

运行时错误

SpringBoot的全局异常拦截实践过程

发起请求

SpringBoot的全局异常拦截实践过程

返回结果

SpringBoot的全局异常拦截实践过程

捕获异常

SpringBoot的全局异常拦截实践过程

业务异常

SpringBoot的全局异常拦截实践过程

发送请求

SpringBoot的全局异常拦截实践过程

返回结果

SpringBoot的全局异常拦截实践过程

捕获异常

SpringBoot的全局异常拦截实践过程

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持China编程(www.chinasem.cn)。

这篇关于SpringBoot的全局异常拦截实践过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156429

相关文章

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

Spring Boot 处理带文件表单的方式汇总

《SpringBoot处理带文件表单的方式汇总》本文详细介绍了六种处理文件上传的方式,包括@RequestParam、@RequestPart、@ModelAttribute、@ModelAttr... 目录方式 1:@RequestParam接收文件后端代码前端代码特点方式 2:@RequestPart接

SpringBoot整合Zuul全过程

《SpringBoot整合Zuul全过程》Zuul网关是微服务架构中的重要组件,具备统一入口、鉴权校验、动态路由等功能,它通过配置文件进行灵活的路由和过滤器设置,支持Hystrix进行容错处理,还提供... 目录Zuul网关的作用Zuul网关的应用1、网关访问方式2、网关依赖注入3、网关启动器4、网关全局变

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso

SpringBoo WebFlux+MongoDB实现非阻塞API过程

《SpringBooWebFlux+MongoDB实现非阻塞API过程》本文介绍了如何使用SpringBootWebFlux和MongoDB实现非阻塞API,通过响应式编程提高系统的吞吐量和响应性能... 目录一、引言二、响应式编程基础2.1 响应式编程概念2.2 响应式编程的优势2.3 响应式编程相关技术

jdk1.8的Jenkins安装配置实践

《jdk1.8的Jenkins安装配置实践》Jenkins是一款流行的开源持续集成工具,支持自动构建、测试和部署,通过Jenkins,开发团队可以实现代码提交后自动进行构建、测试,并将构建结果分发到测... 目录Jenkins介绍Jenkins环境搭建Jenkins安装配置Jenkins插件安装Git安装配

Springboot配置文件相关语法及读取方式详解

《Springboot配置文件相关语法及读取方式详解》本文主要介绍了SpringBoot中的两种配置文件形式,即.properties文件和.yml/.yaml文件,详细讲解了这两种文件的语法和读取方... 目录配置文件的形式语法1、key-value形式2、数组形式读取方式1、通过@value注解2、通过

Java 接口定义变量的示例代码

《Java接口定义变量的示例代码》文章介绍了Java接口中的变量和方法,接口中的变量必须是publicstaticfinal的,用于定义常量,而方法默认是publicabstract的,必须由实现类... 在 Java 中,接口是一种抽象类型,用于定义类必须实现的方法。接口可以包含常量和方法,但不能包含实例

JAVA Calendar设置上个月时,日期不存在或错误提示问题及解决

《JAVACalendar设置上个月时,日期不存在或错误提示问题及解决》在使用Java的Calendar类设置上个月的日期时,如果遇到不存在的日期(如4月31日),默认会自动调整到下个月的相应日期(... 目录Java Calendar设置上个月时,日期不存在或错误提示java进行日期计算时如果出现不存在的