springboot实现全局异常处理的方法(住家饭系统)

 更新时间:2025年05月06日 10:22:56   作者:小小奶酪可笑可笑  
住家饭系统将异常类型分为客户端异常(ClientException),系统异常(ServiceException),远程调用异常(RemoteException),本文给大家介绍springboot实现全局异常处理的方法,感兴趣的朋友一起看看吧

在实际项目开发中,定义全局异常处理至关重要通过全局异常处理器(使@ControllerAdvice@ExceptionHandler注解),可以集中捕获和处理各种异常,避免在每个控制器方法中重复编写异常处理代码。

住家饭系统将异常类型分为客户端异常(ClientException),系统异常(ServiceException),远程调用异常(RemoteException)。类结构图如下:

我们需先定义一个抽象异常类 AbstractException ,该抽象类继承自 RuntimeException 类,通过该类约束异常类行为。

/**
 * 抽象项目中的三类异常,客户端异常、服务端异常和远程服务调用异常
 */
@Data
public abstract class AbstractException extends RuntimeException{
    public final String errorCode;
    public final String errorMsg;
    public AbstractException(String errorMsg, Throwable throwable, IErrorCode errorCode) {
        super(errorMsg, throwable);
        this.errorCode = errorCode.code();
        this.errorMsg = Optional.ofNullable(StringUtils.hasLength(errorMsg) ? errorMsg : null).orElse(errorCode.msg());
    }
}

接着在分别定义客户端异常、服务端异常和远程调用异常类。

public class ClientException extends AbstractException{
    public ClientException (IErrorCode errorCode) {
        super(null, null, errorCode);
    }
    public ClientException(IErrorCode errorCode, String errorMsg) {
        super(errorMsg, null, errorCode);
    }
    public ClientException(String message, Throwable throwable, IErrorCode errorCode) {
        super(message, throwable, errorCode);
    }
    @Override
    public String toString() {
        return "ClientException{" +
                "code='" + errorCode + "'," +
                "message='" + errorMsg + "'" +
                '}';
    }
}
public class ServiceException extends AbstractException{
    public ServiceException(String message) {
        this(message, null, BaseErrorCode.SERVICE_ERROR);
    }
    public ServiceException(IErrorCode errorCode) {
        this(null, errorCode);
    }
    public ServiceException(String message, IErrorCode errorCode) {
        this(message, null, errorCode);
    }
    public ServiceException(String message, Throwable throwable, IErrorCode errorCode) {
        super(Optional.ofNullable(message).orElse(errorCode.msg()), throwable, errorCode);
    }
    @Override
    public String toString() {
        return "ServiceException{" +
                "code='" + errorCode + "'," +
                "message='" + errorMsg + "'" +
                '}';
    }
}
public class RemoteException extends AbstractException{
    public RemoteException(String errorMsg, Throwable throwable, IErrorCode errorCode) {
        super(errorMsg, throwable, errorCode);
    }
    @Override
    public String toString() {
        return "RemoteException{" +
                "code='" + errorCode + "'," +
                "message='" + errorMsg + "'" +
                '}';
    }
}

这样,我们就完成了对三大基本异常类的定义。接下来我们需要通过springboot提供的@ControllerAdvice@ExceptionHandler注解来实现全局异常拦截并处理。我们需定义一个GlobalExceptionHandler类,在该类中分别对参数验证异常、应用内抛出的异常和最顶级的Throwable异常进行处理。

Component("globalExceptionHandlerByAdmin")
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 拦截参数验证异常
     */
    @SneakyThrows
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result validExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        FieldError firstFieldError = CollectionUtil.getFirst(bindingResult.getFieldErrors());
        String exceptionStr = Optional.ofNullable(firstFieldError)
                .map(FieldError::getDefaultMessage)
                .orElse(StrUtil.EMPTY);
        log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr);
        return Results.failure(BaseErrorCode.CLIENT_ERROR.code(), exceptionStr);
    }
    /**
     * 拦截应用内抛出的异常
     */
    @ExceptionHandler(value = {AbstractException.class})
    public Result abstractException(HttpServletRequest request, AbstractException ex) {
        if (ex.getCause() != null) {
            log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString(), ex.getCause());
            return Results.failure(ex);
        }
        log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString());
        return Results.failure(ex);
    }
    /**
     * 拦截未捕获异常
     */
    @ExceptionHandler(value = Throwable.class)
    public Result defaultErrorHandler(HttpServletRequest request, Throwable throwable) {
        log.error("[{}] {} ", request.getMethod(), getUrl(request), throwable);
        if (Objects.equals(throwable.getClass().getSuperclass().getSimpleName(), AbstractException.class.getSimpleName())) {
            String errorCode = ReflectUtil.getFieldValue(throwable, "errorCode").toString();
            String errorMessage = ReflectUtil.getFieldValue(throwable, "errorMessage").toString();
            return Results.failure(errorCode, errorMessage);
        }
        return Results.failure();
    }
    private String getUrl(HttpServletRequest request) {
        if (StringUtils.isEmpty(request.getQueryString())) {
            return request.getRequestURL().toString();
        }
        return request.getRequestURL().toString() + "?" + request.getQueryString();
    }
}

今后,我们在项目里抛出的所有异常,都可以被 GlobalExceptionHandler 类捕获并进行相应的处理。

    public void register(UserRegisterReqDTO requestParam) {
        if(ObjectUtils.isEmpty(requestParam)) throw new ClientException(CLIENT_ERROR);
        if (hasUserName(requestParam.getUsername())) {
            throw new ServiceException(USER_NAME_EXIST);
        }
        try {
            int inserted = baseMapper.insert(BeanUtil.toBean(requestParam, UserDao.class));
            if (inserted <= 0) {
                throw new ClientException(USER_SAVE_ERROR);
            }
        }  catch (DuplicateKeyException ex) {
            throw new ServiceException(USER_EXIST);
        }
    }

到此这篇关于springboot实现全局异常处理的文章就介绍到这了,更多相关springboot全局异常处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 新闻列表的分页查询java代码实现

    新闻列表的分页查询java代码实现

    这篇文章主要为大家详细介绍了新闻列表的分页查询java代码实现,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • SpringBoot用ServiceLocatorFactoryBean优雅切换支付渠道

    SpringBoot用ServiceLocatorFactoryBean优雅切换支付渠道

    本文主要介绍了SpringBoot用ServiceLocatorFactoryBean优雅切换支付渠道,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-10-10
  • SpringBoot模板引擎之Thymeleaf的使用

    SpringBoot模板引擎之Thymeleaf的使用

    这篇文章主要介绍了SpringBoot模板引擎之Thymeleaf的使用,模板引擎是以业务逻辑层和表现层分离为目的的,将规定格式的模板代码转换为业务数据的算法实现,它可以是一个过程代码、一个类,甚至是一个类库,需要的朋友可以参考下
    2023-10-10
  • Spring Boot教程之必须了解的核心概念

    Spring Boot教程之必须了解的核心概念

    这篇文章主要介绍了Spring Boot系列教程中的关于学习Spring Boot必须了解的核心概念的相关资料,需要的朋友可以参考下
    2022-08-08
  • java 教你如何给你的头像添加一个好看的国旗

    java 教你如何给你的头像添加一个好看的国旗

    这篇文章主要介绍了java 教你如何给你的头像添加一个好看的国旗,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • JAVA缓存的使用RedisCache、LocalCache、复合缓存的操作

    JAVA缓存的使用RedisCache、LocalCache、复合缓存的操作

    RedisCache是基于Redis的缓存,数据存储在内存中,并且可以被多个应用实例共享,属于分布式缓存,本文给大家介绍JAVA缓存的使用RedisCache、LocalCache、复合缓存的相关操作,感兴趣的朋友跟随小编一起看看吧
    2026-02-02
  • java的MybatisPlus调用储存过程的返回数据问题

    java的MybatisPlus调用储存过程的返回数据问题

    这篇文章主要介绍了java的MybatisPlus调用储存过程的返回数据问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Spring Boot2.x如何自定义Endpoint

    Spring Boot2.x如何自定义Endpoint

    这篇文章主要介绍了Spring Boot2.x如何自定义Endpoint,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • 如何避免在Java 中使用双括号初始化

    如何避免在Java 中使用双括号初始化

    这篇文章主要介绍了如何避免在Java中使用双括号初始化,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • SpringBoot生成条形码的方案详解

    SpringBoot生成条形码的方案详解

    在Spring Boot, Spring Cloud 项目中整合ZXing库来生成条形码在特定行业也是一个常见需求,ZXing是google开源的一个功能强大的Java库,专门用于二维码/条形码等的生成与解析,所以本文给大家介绍了SpringBoot生成条形码的方案,需要的朋友可以参考下
    2024-08-08

最新评论