当前位置: 首页 > 知识库问答 >
问题:

java - springboot全局异常处理器处理顺序是什么?

商焕
2024-10-07

springboot全局异常处理器处理顺序问题
在使用异常处理器时,代码


package com.ecode.handler;


import com.ecode.constant.MessageConstant;
import com.ecode.exception.BaseException;
import com.ecode.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.sql.SQLException;

/**
 * 全局异常处理器,处理项目中抛出的业务异常
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 捕获业务异常
     * @param ex
     * @return
     */
    @ExceptionHandler
    public Result exceptionHandler(BaseException ex){
        log.error("业务异常:{}", ex.getMessage());
        return Result.error(ex.getMessage());
    }

    /**
     * @param ex 数据库异常
     * @return 后端统一返回结果
     */
    @ExceptionHandler
    public Result exceptionHandler(SQLException ex){
        String message = ex.getMessage();
        log.error("数据库异常:{}",message);
        if (message.contains("Duplicate entry")){
            String username = message.split(" ")[2];
            return Result.error(username + MessageConstant.ALREADY_EXISTS);
        }else {
            return Result.error(MessageConstant.SQL_UNKNOWN_ERROR);
        }
    }

    @ExceptionHandler
    public Result exceptionHandler(Exception ex){
        log.error("服务器异常:{}",ex.getMessage());
        return Result.error("服务器异常");

    }

}

当出现数据库异常时,返回

2024-10-05 22:56:13.798 ERROR 26216 --- [nio-8080-exec-4] c.ecode.handler.GlobalExceptionHandler   : 服务器异常:
### Error updating database.  Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'xu*****@foxmail.com' for key 'user.邮箱'
### The error may exist in com/ecode/mapper/UserMapper.java (best guess)
### The error may involve com.ecode.mapper.UserMapper.insert-Inline
### The error occurred while setting parameters
### SQL: INSERT INTO user  ( username, password,  email, status, name, profile_picture,    score,  create_time, update_time )  VALUES  ( ?, ?,  ?, ?, ?, ?,    ?,  ?, ? )
### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'xu****@foxmail.com' for key 'user.邮箱'
; Duplicate entry 'xu*****@foxmail.com' for key 'user.邮箱'; nested exception is java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'xu*******@foxmail.com' for key 'user.邮箱'

按理说越精确优先级越高,SQLIntegrityConstraintViolationException继承SQLException,为什么返回的却是被Exception异常捕获?求解答

我尝试注释Exception异常捕获,此时异常能被SQLException捕获

共有2个答案

邓宜年
2024-10-07

你的代码就是错误的,SQLException及其子类SQLIntegrityConstraintViolationException是个检查时异常,你在运行时捕获,是捕获不到的,这个函数永远不会执行的

/**
 * @param ex 数据库异常
 * @return 后端统一返回结果
 */
@ExceptionHandler
public Result exceptionHandler(SQLException ex){
    String message = ex.getMessage();
    log.error("数据库异常:{}",message);
    if (message.contains("Duplicate entry")){
        String username = message.split(" ")[2];
        return Result.error(username + MessageConstant.ALREADY_EXISTS);
    }else {
        return Result.error(MessageConstant.SQL_UNKNOWN_ERROR);
    }
}

你在日志中看到SQLIntegrityConstraintViolationException错误,是MyBatis输出时,从MyBatisSystemException.getCause()得到的,实际的异常在返回到控制层时,已经经过了spring-jdbcMyBatis的包装处理,你可以改成这样看看具体的异常类型

@ExceptionHandler
public Result exceptionHandler(Exception ex){
    log.error("服务器{}异常:{}",ex.getClass(),ex.getMessage());
    return Result.error("服务器异常");

}
黄向明
2024-10-07

在Spring Boot中,全局异常处理器的处理顺序主要基于几个因素,但最关键的是方法上的@ExceptionHandler注解指定的异常类型以及这些异常类型之间的继承关系。当你定义了多个@ExceptionHandler方法用于捕获不同类型的异常时,Spring会根据异常的具体类型以及你定义的捕获顺序(实际上是方法的定义顺序,但更准确地说是类型匹配度)来决定调用哪个处理器。

在你的例子中,虽然SQLIntegrityConstraintViolationException继承自SQLException,但Spring在决定调用哪个@ExceptionHandler方法时,并不是简单地按照继承关系从基类到子类进行匹配,而是首先查找与异常类型最精确匹配的方法。然而,由于Java的类型擦除和Spring的异常解析机制,有时候可能会出现一些不太直观的行为。

然而,在大多数情况下,如果SQLIntegrityConstraintViolationException被抛出,并且你有一个专门捕获SQLException的方法,Spring会优先调用这个更具体的方法(即捕获SQLException的方法),因为它更精确地匹配了异常的类型。但在你的情况下,如果SQLIntegrityConstraintViolationException被捕获为Exception而不是SQLException,这通常意味着有其他因素在起作用。

这里有几个可能的原因:

  1. 异常包装SQLIntegrityConstraintViolationException可能被另一个异常包装(比如RuntimeExceptionException),这取决于你的应用程序和Spring Boot的配置。如果它被包装了,那么它可能不再直接匹配SQLException处理器。
  2. 方法签名冲突:虽然这在你的代码中不明显,但理论上如果Spring在解析@ExceptionHandler方法时遇到某种签名冲突(尽管这在这个案例中不太可能),它可能无法正确选择方法。
  3. 配置或版本问题:有时候,Spring Boot的不同版本或特定的配置设置可能会影响异常处理的行为。
  4. 其他@ExceptionHandler干扰:虽然你的示例中没有显示,但如果你的应用中有其他@ExceptionHandler定义在其他类或同一个类中,它们也可能影响异常处理的顺序。
  5. 类加载器问题:在复杂的部署环境中,类加载器问题可能导致Spring无法正确识别异常类型。
  6. 日志中的误导信息:有时候,日志中的异常堆栈跟踪可能包含多个异常,但不一定反映实际被捕获并处理的异常。确保检查的是被实际传递给@ExceptionHandler方法的异常类型。

在你的情况下,当你注释掉捕获Exception的方法时,SQLException的方法能够正确捕获SQLIntegrityConstraintViolationException,这表明SQLIntegrityConstraintViolationException本身没有被包装成另一个类型的异常,并且没有其他@ExceptionHandler干扰。这进一步支持了Spring通常会根据异常类型的精确匹配来选择处理器的观点。

因此,如果你的应用程序在没有注释掉捕获Exception的方法时不能按预期工作,建议检查以下几点:

  • 确保没有其他@ExceptionHandler方法干扰。
  • 检查SQLIntegrityConstraintViolationException是否确实没有被包装成另一个类型的异常。
  • 验证你的Spring Boot版本和配置是否可能导致此行为。
  • 在调试时,仔细检查传递给@ExceptionHandler方法的异常对象的确切类型。
 类似资料:
  • 本文向大家介绍springboot全局异常处理详解,包括了springboot全局异常处理详解的使用技巧和注意事项,需要的朋友参考一下 一、单个controller范围的异常处理 说明: 在controller中加入被@ExceptionHandler修饰的类即可(在该注解中指定该方法需要处理的那些异常类) 该异常处理方法只在当前的controller中起作用 二、全部controller范围内起

  • 问题内容: 有没有一种方法可以在Java中创建全局异常处理程序。我想这样使用: 处理程序可能无法捕获在主体中引发的异常。 马丁 问题答案: 使用Thread.setDefaultUncaughtExceptionHandler。有关示例,请参见Rod Hilton的“ Global Exception Handling ”博客文章。

  • 统一错误处理 文档:https://eggjs.org/zh-cn/tutorials/restful.html 自定义一个异常基类 // app / exceptions / http_exceptions.js class HttpExceptions extends Error { constructor(msg='服务器异常', code=1, httpCode=400) {

  • 本文向大家介绍springboot全局异常处理代码实例,包括了springboot全局异常处理代码实例的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了springboot全局异常处理代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前言: 开发中异常的处理必不可少,常用的就是 throw 和 try catch,这样一个项目到最

  • Middleware: 全局异常处理 我们在岩浆的实例其实已经注意到了,compose 的连接方式,让我们有能力精确控制异常。 Koa中间件最终行为强依赖注册顺序,比如我们这里要引入的异常处理,必须在业务逻辑中间件前注册,才能捕获后续中间件中未捕获异常,回想一下我们的调度器实现的异常传递流程。 <?php class ExceptionHandler implements Middleware

  • 一.问题前置信息 1.1项目配置了token拦截器只重写了preHandle()方法 1.2全局异常处理器只处理了两个token相关的自定义异常 1.3登录接口配置了白名单,请求不会经过拦截器 二.问题描述:页面调用登录接口,内部异常流转到了拦截器的preHandle()方法内? 三.问题详细描述 接口内部调用了org.mybatis.spring包下的SqlSessionTemplate类的se