千家信息网

springboot项目全局异常处理会遇到哪些问题

发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,这篇文章主要介绍了springboot项目全局异常处理会遇到哪些问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。问题一:全局异常抽离
千家信息网最后更新 2025年11月07日springboot项目全局异常处理会遇到哪些问题

这篇文章主要介绍了springboot项目全局异常处理会遇到哪些问题,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

问题一:全局异常抽离出来后,业务错误码如何定义?

之前团队的业务错误码定义是:业务服务前缀 + 业务模块 + 错误码,如果是识别不了的异常,则使用业务前缀 + 固定模块码 + 固定错误码。

之前的全局异常伪代码如下

@RestControllerAdvice@Slf4jpublic class GlobalExceptionBaseHandler {       @ExceptionHandler(Exception.class)    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)    public AjaxResult handleException(Exception e) {        String servicePrifix = "U";        String moudleCode = "001";        String code = "0001";        String errorCode = servicePrifix + moudleCode + code;        String msg = e.getMessage();        if(StringUtils.isEmpty(msg)){            msg = "服务端异常";        }        log.error(msg, e);        return AjaxResult.error(msg, errorCode);    }    }
现在全局异常抽离出来后,那个业务服务前缀如何识别?之前未抽离时,业务服务前缀各个业务服务直接写死在代码里。

当时我们临时的解决方案是通过spring.application.name来解决。因为全局异常代码块抽离出来后,最终还是要被服务引入的。因此获取业务服务前缀的伪代码可以通过如下获取

public enum  ServicePrefixEnum {    USER_SERVICE("U","用户中心");    private final String servicePrefix;    private final String serviceDesc;    ServicePrefixEnum(String servicePrefix,String serviceDesc) {        this.servicePrefix = servicePrefix;        this.serviceDesc = serviceDesc;    }    public String getServicePrefix() {        return servicePrefix;    }    public String getServiceDesc() {        return serviceDesc;    }}
public String getServicePrefix(@Value("${spring.application.name}") String serviceName){      return ServicePrefixEnum.valueOf(serviceName).getServicePrefix();    }

但这种方案其实是存在弊端

弊端一: 通过枚举硬编码,预设了目前了微服务名称,一旦项目改变了微服务名,就找不到服务前缀了。

弊端二: 如果新上线了业务服务模块,这个枚举类还得改动

后面我们在全局异常jar中增加了自定义业务码的配置,业务人员仅需在springboot配置文件配置,形如下

lybgeek:  bizcode:    prefix: U

此时全局异常改造示例形如下

@RestControllerAdvice@Slf4jpublic class GlobalExceptionBaseHandler {            @Autowired    private ServiceCodeProperties serviceCodeProperties;       @ExceptionHandler(Exception.class)    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)    public AjaxResult handleException(Exception e) {        String servicePrifix = serviceCodeProperties.getPrifix();        String moudleCode = "001";        String code = "0001";        String errorCode = servicePrifix + moudleCode + code;        String msg = e.getMessage();        if(StringUtils.isEmpty(msg)){            msg = "服务端异常";        }        log.error(msg, e);        return AjaxResult.error(msg, errorCode);    }}

问题二:全局异常因引入了和业务相同的依赖jar,但jar存在版本差异

如果全局异常直接如下写,是不存在问题。示例如下

@RestControllerAdvice@Slf4jpublic class GlobalExceptionBaseHandler {    @Autowired    private ServiceCodeProperties serviceCodeProperties;        @ExceptionHandler(Exception.class)    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)    public AjaxResult handleException(Exception e) {        String servicePrifix = serviceCodeProperties.getPrifix();        String moudleCode = "001";        String code = "0001";        String errorCode = servicePrifix + moudleCode + code;        String msg = e.getMessage();        if(StringUtils.isEmpty(msg)){            msg = "服务端异常";        }        log.error(msg, e);        return AjaxResult.error(msg, HttpStatus.INTERNAL_SERVER_ERROR.value());    }    @ExceptionHandler(BizException.class)    public AjaxResult handleException(BizException e)    {        return AjaxResult.error(e.getMessage(), e.getErrorCode());    }}

即全局异常直接分为业务异常和Execption这两种,这样划分的弊端在于没办法细分异常,而且也使项目组定义的模块码和业务码没法细分。因此我们也列出常用可以预知的系统异常,示例如下

/**     *参数验证失败     * @param e     * @return     */    @ExceptionHandler(ConstraintViolationException.class)    @ResponseStatus(HttpStatus.BAD_REQUEST)    public AjaxResult handleException(ConstraintViolationException e)    {        log.error("参数验证失败", e);        return AjaxResult.error("参数验证失败", HttpStatus.BAD_REQUEST.value());    }   /**     * 数据库异常     * @param e     * @return     */    @ExceptionHandler({SQLException.class, MybatisPlusException.class,            MyBatisSystemException.class, org.apache.ibatis.exceptions.PersistenceException.class,            BadSqlGrammarException.class    })    @ResponseStatus(HttpStatus.BAD_REQUEST)    public AjaxResult dbException(Exception e) {        String msg = ExceptionUtil.getExceptionMessage(e);        log.error(msg, e);        return AjaxResult.error(msg,HttpStatus.BAD_REQUEST.value());    }    /**     * 数据库中已存在该记录     * @param e     * @return     */    @ExceptionHandler(DuplicateKeyException.class)    @ResponseStatus(HttpStatus.CONFLICT)    public AjaxResult handleException(DuplicateKeyException e)    {        log.error("数据库中已存在该记录", e);        return AjaxResult.error("数据库中已存在该记录", HttpStatus.CONFLICT.value());    }

不过这样导致了一个问题,就是全局异常和业务方使用相同的依赖jar,但存在版本差异时,可能就会存在依赖冲突,导致业务项目启动报错。因此解决方案就是在pom文件加入optional标签。示例如下

            com.baomidou            mybatis-plus-boot-starter            true        

这标签的意思这jar坐标是可选的,因此如果项目中已经有引入该jar的坐标,就直接用该jar的坐标

问题三:引入maven optional标签后,因业务没引入全局异常需要的jar,导致项目启动报错

这个问题的产生:举个示例,我们的业务微服务项目有聚合层,某些聚合层是不需要依赖存储介质,比如mysql。因此这些聚合层项目pom就不会引入类似mybatis相关的依赖。但我们的全局异常又需要类似mybatis相关的依赖,这样导致如果要引用全局异常模块,有得额外加入业务方不需要的jar。

因此springboot的条件注解就派上用场了,利用@ConditionalOnClass注解。示例如下

@RestControllerAdvice@Slf4j@ConditionalOnClass({SQLException.class, MybatisPlusException.class,        MyBatisSystemException.class, org.apache.ibatis.exceptions.PersistenceException.class,        BadSqlGrammarException.class, DuplicateKeyException.class})public class GlobalExceptionDbHandler {    /**     * 数据库异常     * @param e     * @return     */    @ExceptionHandler({SQLException.class, MybatisPlusException.class,            MyBatisSystemException.class, org.apache.ibatis.exceptions.PersistenceException.class,            BadSqlGrammarException.class    })    @ResponseStatus(HttpStatus.BAD_REQUEST)    public AjaxResult dbException(Exception e) {        String msg = ExceptionUtil.getExceptionMessage(e);        log.error(msg, e);        return AjaxResult.error(msg,HttpStatus.BAD_REQUEST.value());    }    /**     * 数据库中已存在该记录     * @param e     * @return     */    @ExceptionHandler(DuplicateKeyException.class)    @ResponseStatus(HttpStatus.CONFLICT)    public AjaxResult handleException(DuplicateKeyException e)    {        log.error("数据库中已存在该记录", e);        return AjaxResult.error("数据库中已存在该记录", HttpStatus.CONFLICT.value());    }}

@ConditionalOnClass这个注解的作用就是如果classpath存在指定的类,则该注解上的类会生效。

同时这边有个细节点,就是全局异常可能就得细分,即把原来的大一统的全局异常,按业务场景分开,比如存储介质相关的存储异常,web相关异常

感谢你能够认真阅读完这篇文章,希望小编分享的"springboot项目全局异常处理会遇到哪些问题"这篇文章对大家有帮助,同时也希望大家多多支持,关注行业资讯频道,更多相关知识等着你来学习!

业务 全局 服务 项目 问题 数据 数据库 前缀 示例 模块 代码 就是 弊端 注解 篇文章 错误 参数 坐标 方案 标签 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 批量合并gdb数据库一个图层 用知网查重会进入数据库吗 三级网络技术用什么软件 三河市城宽网络技术有限公司 软件开发计划合理 项目管理对软件开发意义 巨邦网络技术有限公司 提高网络技术的学校 网络安全技术 杂志 青浦区项目数据库服务收费标准 暗黑代理服务器 北京工业软件开发如何收费 通州网络技术 手机电脑版泰拉瑞亚服务器怎么进 elk可以替代关系型数据库吗 安卓软件开发页面设计 主题班会网络安全开头语 信易杭州互联网科技 欣达网络技术有限公司 微信小程序上传图片到服务器失败 寒假网络安全是什么意思 庚顿实时数据库怎么用 我的世界服务器未来战争怎么圈地 在线翻译平台数据库设计 企业 oa系统服务器地址怎么填 余姚一站式软件开发公司 ftp服务器的权限设置 昆明分布式服务器方案 广州蜀山网络技术 数据库技术压题
0