【雨夜】业务中 自定义异常用 Exception 还是 RuntimeException? 为什么?

news2025/7/19 16:51:10

今天和同事 聊了下异常 相关的事,整理在此

 目前公司中使用的 自定义异常是 extend RuntimeException 

目录 思维导图

继承异常

我们在业务开发中 继承异常是extend RuntimeException 还是 Exception呢

一想 这肯定是 RuntimeException 啊,但是这是为什么呢? 选择 Exception 不行么?

RuntimeException和Exception区别

聊这个之前 我们需要确定我们的需求是什么

需求

需求是 自定义一个 业务中使用的异常

确定了需求 接下来我们就要分析

到这里 知道 我们业务开发 基本都是运行时异常 所以用 RuntimeException

到这里有一个疑问,我们平时 IO操作的时候 动不动就爆红 让我们throws 或者 try catch,是哪个属性做到的?

首先看一段代码

但是这个异常是因为createNewFile 方法 主动抛出的异常 和 是不是 RuntimeException 没关系啊

public boolean createNewFile() throws IOException {
    SecurityManager security = System.getSecurityManager();
    if (security != null) security.checkWrite(path);
    if (isInvalid()) {
        throw new IOException("Invalid file path");
    }
    return fs.createFileExclusively(path);
}
复制代码

是有关系的,非RuntimeException 都是需要throws 或者try catch 但是这个不是自动的,是开发人员写代码的时候 需要做的,代表这个异常是 必须被检查的

而其他需要注意的 引用 程序设计语言原理 书中 第434页的一部分

这块对于业务中使用哪个 先留一个疑问 往下看

怎么才算其他RuntimeException 代码上有什么区别?

没有区别就是一个继承 RuntimeException,另一个继承 Exception,别的没有了

事务中拦截异常

我们到这 知道了继承异常应该用RuntimeException,但是我们应该知道 阿里规约手册 中有这么一段

1、让检查异常也回滚:你就需要在整个方法前加上@Transactional(rollbackFor=Exception.class)

2、让非检查异常不回滚:
需要加入@Transactional(notRollbackFor=RunTimeException.class)

3、不需要事务管理(or 日志丢失)
需要加入@Transactional(propagation=Propagation.NOT_SUPPORTED)

为什么这么设置?

Exception类下面除了runtimeException还有SQLException和ioException

如果方法没有抛出runtimeException 而是抛出 SQLExceptionioException那么事务是不会回滚的

那么这就结束了吗?在我们编码过程中,如果方法要抛出一些可检查异常时是需要throws进行显式指定异常类的

那么问题来了,我们都知道方法签名中默认是throws RuntimeException,已知SqlException不是RuntimeException的子类

小总结

@Transactional(notRollbackFor=RunTimeException.class) 是因为抛弃了 IO异常和 SQL异常等情况,所以 我们 应该用 Transactional(rollbackFor=Exception.class)

为什么 不是 rollbackFor = Throwable.class 呢

不需要显式指定 rollbackFor = Throwable.class ,因为如果发生错误,spring将默认回滚事务。**

原本就是除了 非RuntimeException 别的已经在事务管理中了

在其默认配置中,Spring Framework的事务基础结构代码仅标记事务对于运行时回滚,未经检查的异常;也就是说,抛出的异常是RuntimeException的实例或子类。  (错误也会 - 默认情况下会导致回滚) 。从事务方法抛出的已检查异常不会导致在默认配置中回滚。**

或者查看 DefaultTransactionAttribute

public boolean rollbackOn(Throwable ex) { 
        return (ex instanceof RuntimeException || ex instanceof Error); 
}
复制代码

具体位置 docs.spring.io/spring-fram…

总结下来事务方面应该使用 @Transactional(rollbackFor=Exception.class)

写到这 你肯定想问 你百度一篇文档 就告诉我们 默认Error就在事务管理中? 你做过实验么?

小实验

 @Transactional(rollbackFor = Exception.class)
    public void a() {
        Version version = new Version();
        version.setVersionName("test0123120123");
        version.setProjectId(1L);
        versionService.save(version);
        try{
            int i = 1 /0;
        }catch (Exception e){
            //抛出Error 异常
            throw new OutOfMemoryError("test 内存溢出 error");
        }
    }
复制代码

执行结果 保证了事务,数据库没有新增

到这里 你肯定有疑问

你对 rollbackFor 设置为 Exception,是不是原来的 事务拦截就失效了(Error 和 Runtime异常),到底是 覆盖之前的数据 还是 扩展原来的数据(Error 和 Runtime异常)

这是因为 触发了 下面的判断,初步得到结论 是 扩展

public boolean rollbackOn(Throwable ex) { 
        return (ex instanceof RuntimeException || ex instanceof Error); 
}
复制代码

继续实验

if (winner == null) {
    //说明 rollbackFor 没有命中这个异常,执行默认的判断 (ex instanceof RuntimeException || ex instanceof Error)
    //如果是 Error 异常 走这个分支
    logger.trace("No relevant rollback rule found: applying default rules");
    return super.rollbackOn(ex);
} else {
    //说明 rollbackFor 命中了 这个异常
    return !(winner instanceof NoRollbackRuleAttribute);
}
复制代码

到这 我们就要 往回分析 winner 是怎么赋值的呢?

额外的学习点

怎么判断异常

RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
if (this.rollbackRules != null) {
    Iterator var4 = this.rollbackRules.iterator();

    while(var4.hasNext()) {
        RollbackRuleAttribute rule = (RollbackRuleAttribute)var4.next();
        //获取这个异常的 int 等级 ,越小越优先
        int depth = rule.getDepth(ex);
        if (depth >= 0 && depth < deepest) {
            deepest = depth;
            winner = rule;
        }
    }
}
复制代码

List RollbackRuleAttribute rollbackRules 的值是怎么初始化的

其实一句话 怎么从 @Transactional(rollbackFor=Exception.class) 变为 list对象的? 走的是SpringTransactionAnnotationParser 类中的 parseTransactionAnnotation 方法

小总结

一句话 对事务是否生效的判断 是 在 (ex instanceof RuntimeException || ex instanceof Error) 基础上,进行了 扩展的判断

其他中间件 对异常的应用场景 和 为什么?

先看nacos

//检查 异常
public class NacosException extends Exception {
}
复制代码

在使用的时候

有throws 的 也有 try catch的

那么 是什么抛出Nacos 异常的呢?

private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass)
        throws NacosException {
    try {
        xxx
    } catch (Exception e) {
        throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e);
    }
    throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response");
}
复制代码

rocketmq

先看一个 运行时异常

运行时异常
public class AclException extends RuntimeException {
}
复制代码

使用

public static void verify(String netaddress, int index) {
    if (!AclUtils.isScope(netaddress, index)) {
        //运行的时候 发生异常
        throw new AclException(String.format("Netaddress examine scope Exception netaddress is %s", netaddress));
    }
}
复制代码

再看一个 检查异常

public class MQClientException extends Exception {
}
复制代码
throws MQClientException, RemotingException, MQBrokerException
复制代码

使用

public TransactionSendResult sendMessageInTransaction(final Message msg,
    final LocalTransactionExecuter localTransactionExecuter, final Object arg)
    throws MQClientException {
    TransactionListener transactionListener = getCheckListener();
    if (null == localTransactionExecuter && null == transactionListener) {
        //如果为空 client 异常
        throw new MQClientException("tranExecutor is null", null);
    }
 }
复制代码

总结

中间件中大部分都是 client 连接失败,远程连接超时,server端异常 这种,属于检查时异常,所以应该 extend Exception

但是

  1. 业务开发的时候 大部分都是 判断空,属于运行时异常 推荐 RunntimeException
  2. 检查时异常 需要一直 throws,从代码整洁度上 推荐 RunntimeException

总结

  1. 远程连接的使用 / 异常 使用检查时异常,让另一端能感受到异常
  2. 业务中的代码使用 运行时异常

我还是推荐业务使用 extends RuntimeException,其余的就需要根据业务场景选择了

思考

 ok 那你说 远程连接使用 检查时异常,那feign 属于远程rpc,他的异常就必须是 检查时异常么?为什么? 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

kingdee漏洞金蝶云星空存在弱口令漏洞

kingdee漏洞简介 金蝶云星是基于云计算、大数据、社交、人工智能、物联网等前沿技术研发的新一代战略性企业管理软件。金蝶云星空存在弱口令漏洞&#xff0c;攻击者利用该漏洞登录系统后台&#xff0c;获取敏感信息。 CNNVD编号&#xff1a;CNVD-2022-15854危害等级&#xff…

【C++笔试强训】第二十七天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6;&a…

使用PostMan测试WebService接口

在浏览器中输入 http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl 查看是否启动服务&#xff0c;确保WS地址可用。通过地址获取获取WSDL地址相关参数&#xff0c;并找空间命名&#xff0c;这个位置是固定的&#xff0c;下面会用到&#xff0c;这里是 http://WebXml.c…

62岁腾格尔要开线上演唱会,直播间能否唱《遥远的地方》成焦点

人生六十岁一花甲&#xff0c;著名草原歌手腾格尔&#xff0c;在经历了人生的风风雨雨后&#xff0c;如今已经步入了六十二岁的年龄。虽然已经年过花甲&#xff0c;但是腾格尔老师依旧意气风发&#xff0c;他的个人线上演唱会&#xff0c;也准备在11月19日与大家见面。 在网友的…

【C++布隆过滤器和哈希切分】

目录 1.布隆过滤器概念 2.布隆过滤器优点 3.哈希切分 位图储存的类型只能是整形&#xff0c;有没有储存自定义类型或者是字符串类型的“位图”呢&#xff1f; 1.布隆过滤器概念 步隆过滤器是由布隆&#xff08;Burton Howard Bloom&#xff09;在1970年提出的 一种紧凑型的…

从3D ToF到智能座舱系统方案,英飞凌如何赋能未来出行?

当前&#xff0c;全球汽车产业正处于大变革和市场重构的关键时期&#xff0c;智能汽车新时代的大幕已然拉开。 “智能座舱、人机交互已经成为车载信息娱乐系统‘智能化’的新亮点。”英飞凌相关负责人表示。在汽车智能化、网联化、电动化等大趋势下&#xff0c;越来越多的创新…

【C++进阶】map和set——下篇(红黑树的学习以及封装map和set)

&#x1f387;C学习历程&#xff1a;入门 博客主页&#xff1a;一起去看日落吗持续分享博主的C学习历程博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 也许你现在做的事情&#xff0c;暂时看不到成果&#xff0c;但不要忘记&…

ggplot2图形简单绘制

文章目录一、所用函数1. sort、rank、order用法2. arrange 用法3. reorder用法4. cumsum 用法5. interaction用法二、散点图三、折线图、直方图、箱线图四、柱状图1. 单一变量 &#xff08;统计单一变量的属性值分布&#xff09;2. 单一变量fill &#xff08;列联表&#xff09…

mybatis详解

学习到现在,作为我们的java萌新来说,是时候来学习一点偷懒的武林秘籍了,今天我给大家介绍的就是在无上秘宝--mybatis持久型框架. 学习一个东西之前,我们得首先了解他的前世今生... 前世:原是Apache的一个开源项目iBatis, 2010年6月这个项目由ApacheSoftware Foundation 迁移到…

SQL注入靶机练习:BUU SQL COURSE 1

SQL注入靶机练习&#xff1a;BUU SQL COURSE 1一、SQL注入知识点二、前置知识三、SQL注入测试的一般步骤四、解题过程一、SQL注入知识点 可参考SQL注入详解 二、前置知识 参考来源&#xff1a;渗透攻防Web篇-深入浅出SQL注入 mysql5.0以上版本中存在一个重要的系统数据…

无敌,全面对标字节跳动2-2:算法与数据结构突击手册(leetcode)

学习是一种基础性的能力。然而&#xff0c;“吾生也有涯&#xff0c;而知也无涯。”&#xff0c;如果学习不注意方法&#xff0c;则会“以有涯随无涯&#xff0c;殆矣”。 学习就像吃饭睡觉一样&#xff0c;是人的一种本能&#xff0c;人人都有学习的能力。我们在刚出生的时候…

某城市道路桥梁设计计算书+cad图纸

第一章 工程概述 41 1.1设计题目 41 1.2 设计资料 42 1&#xff0e;3 桥梁设计的基本要求 42 1.3.1 使用上的要求&#xff1a; 42 1.3.2 经济上的要求&#xff1a; 42 1.3.3 结构和尺寸上的要求&#xff1a; 42 1.3.4 施工上的要求&#xff1a; 42 1.3.5美观上的要求&#xff1…

Aspose.PDF for Java Crack by Xacker

Aspose.PDF for Java 是一个本地库&#xff0c;使开发人员能够将 PDF 处理功能添加到他们的应用程序中。API 可用于构建任何类型的 32 位和 64 位应用程序&#xff0c;以在不使用 Adob​​e Acrobat 的情况下生成或读取、转换和操作 PDF 文件。 Aspose.PDF for Java API 允许执…

嵌入式(驱动开发)(内核内存管理)

一、内核内存管理框架 内核将物理内存等分成N块4KB&#xff0c;称之为一页&#xff0c;每页都用一个struct page来表示&#xff0c;采用伙伴关系算法维护 内核地址空间划分图&#xff1a; 3G~3G896M&#xff1a;低端内存&#xff0c;直接映射 虚拟地址 3G 物理地址 ​ 细…

Disk Drill for Mac v5.0.1043 苹果数据恢复软件

Disk Drill Enterprise for Mac&#xff0c;数据恢复软件&#xff0c;Disk Drill for Mac 解锁钥匙&#xff0c;Disk Drill for Mac序列号&#xff0c;顶尖的Mac数据恢复软件&#xff0c;你需要恢复在Mac OS X中已经删除的文件吗&#xff1f;比如重要商业文档、音乐、图片或者视…

Linux开发工具(5)——git

文章目录git版本控制器git是什么git的操作clone仓库到本地上传本地文件到gitgit版本控制器 git是什么 标题也说了git就是一个版本控制器&#xff0c;版本控制器是用来保存一个文件的历史版本&#xff0c;如果有需要可以进行回溯&#xff0c;也就是取得以前编辑完成的版本。 比…

Flutter基础知识

const 常量构造函数&#xff0c;如果控件是&#xff0c;则前面加&#xff1b;如果常量构造函数包含有非常量构造函数&#xff0c;则不加&#xff0c;内部是的控件单独加 1.Flutter学习网址 Flutter 中文官网 https://flutter.cn/ Flutter 官网&#xff1a;https://flutter.de…

移动WEB开发之流式布局--移动端基础

浏览器现状 PC端常见浏览器 360浏览器、谷歌浏览器、火狐浏览器、QQ浏览器、百度浏览器、搜狗浏览器、IE浏览器。 移动端常见浏览器 UC浏览器&#xff0c;QQ浏览器&#xff0c;欧朋浏览器&#xff0c; 百度手机浏览器&#xff0c;360安全浏览器&#xff0c;谷歌 浏览器&…

数学建模笔记

文章目录层次分析法——评价类问题原理代码插值算法原理代码拟合算法原理代码相关系数person 相关系数spearman 相关系数二分类和多分类二分类逻辑回归费希尔判别多分类Fisher判别聚类模型分析时间序列分析层次分析法——评价类问题 原理 1. 首先确定评价的目标&#xff0c;可…

智慧医院解决方案-最新全套文件

智慧医院解决方案-最新全套文件一、背景概述二、建设面临的挑战1、网络安全威胁2、关键业务宕机风险3、核心业务数据丢失风险三、总体建设架构四、建设方案五、获取 - 智慧医院全套最新解决方案合集一、背景概述 医院医疗信息化建设已走过两个阶段&#xff1a;第1阶段就是建立…