发现Spring事务的一个实锤bug,官方还拒不承认?你来评评理... (下)

简介: 发现Spring事务的一个实锤bug,官方还拒不承认?你来评评理... (下)

好戏上演


我再次在 issues 里面搜索 RollbackRuleAttribute,会发现多了一条内容:

image.png

image.png

image.png

好戏就藏在这个 issues 里面的,一起看一下官方是怎么“反复横跳”。

https://github.com/spring-projects/spring-framework/issues/28098

首先,是一个叫做 snicoll 的哥们把这个 issues 的标题改了一下:

image.png

别问,问就是大佬,Spring 和 Spring Boot项目核心维护人员。

然后隔了几天,这个问题的标题又被另外一个大佬,简单的修改了一下:

image.png

仅仅是把 use contains 修改为了 uses contains(),把 equals 修改为了 equals()

这个小细节完美的体现了 Spring 框架的严谨之处,可以说是非常的严谨了。

还没进入到问题解答环节,先把问题的“错别字”给修改了。

image.png

接着就进入了官方答疑环节。

说了下面这么大一堆内容,但是根本不要慌,你知道的我的 English level 是非常的 high 的。这一堆内容分为三大部分,我会一点点的给你说明白:

微信图片_20220429082705.png

image.png

一上来就是个英语长句,但是根本不要怕。

你看他先是简明扼要的提到了一个短语“by design”,也就是“设计如此”。

整个翻译过来大概就是这样的。

这个地方我们要用 contains() 方法呢?这其实是经过考虑的。

那么是基于什么考虑呢?

在 XML 配置文件中,用户通常指定自定义异常类型的简单名称,而不是全路径类名。

啥意思呢?

他给了一个文档中的 xml 配置示例:

image.png

image.png

这里我也不得不感慨一句:以前基于 xml 开发的时候是真的麻烦,每次都要去系统项目里面拷一份配置出来,所以我还是很感谢 SpringBoot 的出现的。

这里他想表达一个什么意思呢?

在我的 xml 配置中,关于 rollback-for 属性。他提到的 simple name 就是 AgeException。而 fully qualified class name 就是 com.example.transactional.exception.AgeException。

就是说这里是不限制用户填什么的。

如果用户填的是 simple name,我们也应该让其生效,所以必须要使用 contains() 方法。

以我理解,这个地方和 @Transactional 注解里面的 rollbackForClassName 属性的用法是一样,而这是一个历史遗留问题,是当年一个不好的设计。

但是我认为不能说考虑不周,毕竟别人也很难想到你会按照那么奇怪的方式去命名异常类啊!

总之这一段话他解释了为什么会用 contains() 方法,为什么不能用 equals() 方法。

和我们前面分析的基本一致,只是我们没有想到 XML 的配置方式。

第二段,他开始从文档的角度来解释这个问题。

image.png

叫我们关注一下 RollbackRuleAttribute 上的 Javadoc 描述。

这里有一个“NB”,不是我们常常说的牛逼,而是一个缩写:

image.png

你看,又在我这里学到一个用不上的英文知识。

我们接着看,主要关注我划线的两句。

第一句是说:由于使用的 contains() 方法,“Exception” 几乎可以匹配任何规则,并且可能会隐藏其他规则。但是“java.lang.Exception”这个全路径的字符串,那么匹配范围就小了很多了。

第二句是说:由于使用的 contains() 方法,对于 “BaseBusinessException” 等不寻常的异常名称,不需要使用类的全路径名称。

所以,第二段他想表达的是:文档上我们已经说过了,对于匹配规则,要仔细思考,要非常的小心:

image.png

u1s1,他确实写了,但是你觉得你会看吗?

第三段就很简单了:


image.png

看到 its subclasses, and its nested classes. 就知道这是我们前面“铺垫一波”小节说过的部分。

所以你现在知道我为什么给你铺垫了吧?

如果不给你铺垫一波,你突然看到一个内部类的单词 nested classes,你说你一下反应得过来吗?

你要永远相信我的行文结构。

image.png

好了,现在看另外一句我标注的地方,翻译过来是说:在当前的实现中最后一句话并没有遵守。

这里的“最后一句话”就是指 RollbackRuleAttribute 的 Javadoc 的最后一句,也就是 ... its subclasses, and its nested classes 这句。

当然没遵守了。

我写的 Demo 里面的两个异常即不存在子类父类的关系,也不存在内部类的关系。

所以我觉得很纳闷:这个 Javadoc 和我的问题之间并不存在关系,或者说并不冲突啊。前面我也说了,关于这部分的 Javadoc 我觉得是没有毛病的。如果你想要从修改文档的角度来解决这个问题,也不应扯到子类,内部类啥的,应该是完全另起一行才对。

但是具体怎么解决,他并没有立即表态,而是把这个 issues 放到了 Triage Queue 里面:

image.png

也就是说官方把这个 issues 放到了“待分类的”一个队列里面,说明他目前是了解到了问题的所在,但是具体应该怎么解决,还没有定论,有待商榷。

隔了一天这个老哥又来表态了,开始“横跳”:

image.png

他说他又想了一下,需要更正他之前的说法:RollbackRuleAttribute(Class) 构造函数的 Javadoc 是 mostly correct,也就是基本没毛病的。需要改进的是关于回滚规则上的描述。

总之他还是想从文档的角度来修复这个问题。

但是解释了我前面的疑惑:即使从修改文档的角度来解决这个问题,也不应扯到子类,内部类啥的,应该是完全另起一行才对。

他这里的“回滚规则”也就是“另起一行”。

接着,他对任务的状态进行了流转:

image.png

Transaction rollback rules may result in unintentional matches for similarly named exceptions

事务回滚规则可能会导致无意中匹配到名称相似的例外情况

其实如果让我来处理这个问题,我大概率也是会从文档的角度入手,并且最多加一点提醒日志,毕竟这是你使用不规范导致的。

而且我文档上已经说明有“坑”了,你自己没看踩进去了,这怪不得我呀。

但是在和这个读者表达了我的观点之后,他提出的不一样的看法:

image.png

他觉得使用者大多并不关注日志,主张抛出异常的方式进行强提醒:

于是他在 issues 上表达了自己的看法:

image.png

image.png

他说:老铁,我同意你关于“需要更精准的匹配规则”的观点。

我们会修复 5.3.x 的文档描述。

然后在 6.0 版本中,我们会改进一版代码。

具体来说是这样的:

  • 如果异常模式是以字符串形式提供的。例如,在 XML 配置中或通过 @Transactional(rollbackForClassName = "example.CustomException") 配置,那么现有的 contains() 逻辑将继续被使用。
  • 如果一个具体的异常类型是以类引用的形式提供的。例如,通过 @Transactional(rollbackFor = example.CustomException.class),将会有新的逻辑。它完全采用配置上提供的类型信息,从而避免了在 example.CustomException(没有2)被提供为异常类型时,与 example.CustomException2 的意外匹配。

他这里提到的 CustomException 和 CustomException2,其实是他的测试用例里面的代码。类比于我们前面的 AgeException 和 AgeExceptionOver18 这两个异常。

接着,他对这个 issues 进行了重新分类,从“文档”类型,修改为了“enhancement”类型:

image.png

对于事务回滚规则,应该使用异常的类型信息,而不是用模式匹配。

本来故事到这里都已经是大结局了,我写到这里的时候就准备收尾了。

想着收尾不着急,先睡一觉再说。

结果...

第二天早上起来,他!又!更!新!了!

我还得补一段内容。


image.png


最后一集


早上起来,我一刷新页面,发现官方针对这个 issues 进行了最后一次提交:

image.png

这次 issues 的标题,最后定格为:

Support type-safe transaction rollback rules

支持类型安全的事务回滚规则

而这次对应的代码提交链接是这样的:

https://github.com/spring-projects/spring-framework/commit/c1033dbfb3609f3b3fe002d7b582b3302620c05a

里面写了很长一段的内容,来描述这次提交的背景,但是基本上都是我前面写过的东西的总结:


微信图片_20220429083030.png


结合我前面写的东西,我给你翻译翻译:

首先我觉得是在事务模块里面创造一个新的概念:type-safe rollback rules,类型安全的回滚规则。

在这次提交之前,只有一种事务回滚机制, Pattern-based rollback rules,即基于匹配模式的回滚规则。

而官方说基于匹配模式的回滚规则,会带来三种意料之外的匹配情况:

  • 不同包中的相同命名的异常类,会被意外匹配上。比如,example.client.WebException 和 example.server.WebException 都会与 “WebException” 模式匹配。
  • 在同一个包中有类似命名的异常,这里说的相似是指当一个给定的异常名称是以另一个异常的名称开头时。例如:example.BusinessException 和 example.BusinessExceptionWithDetails 都与 “example.BusinessException”模式匹配。
  • 嵌套异常,也就是当一个异常类被声明在另一个异常类里面的时候。例如:example.BusinessException 和 example.BusinessException$NestedException 都会与 “example.BusinessException” 匹配上。

第一种没啥说的,请使用全路径名称去避免。

第二种就是我们文章中的例子,需要通过修改代码解决。

第三种内部类的情况我也在前面铺垫过了。但是当时的解决方案是仅增加文档中对应的描述。

但是现在,你看他怎么说的:

image.png

image.png

image.png

当 exceptionType 字段,即全路径名称不为空的时候,使用 equals() 方法,也就是type-safe rollback rules。否则使用 contains() 方法,也就是 Pattern-based rollback rules。

另外,关于 Javadoc 上的很多描述也发生了变化,我就不一一举例了,强烈建议你自己去看看这次提交。

我只特别拿出一处变化来给你看看:

image.png

去掉了“内部类”,改成了“类型安全”。

至此,问题得到解决。

但是在 XML 里面或者用 @Transactional 注解里面的 rollbackForClassName 属性,也就是使用匹配模式的时候,还是会有意料之外的匹配情况。

官方:反正我在文档上说清楚了,你要是还踩坑,那就怪得不我了?

最后,再插一个关于编程规范的事儿。

你想这次这个问题完全是因为你有两个这样的异常类名称引起的:

AgeException

AgeExceptionOver18

而对于异常类,我们都约定成俗的要求必须以“Exception”结尾。

包括阿里巴巴 Java 开发手册在命名风格里面也特意提到了这一点,且是强制要求:

image.png

它告诉我们规则就是拿来打破的,如果你不打破规则,永远也踩不到这个坑,也就不会推动 Spring 的改动。

打破规则,这是你的一小步,却是开源世界的一大步。

所以,兄弟们,铁子们,不要墨守成规,要打破...

image.png

最后,文章首发于公众号[why技术],欢迎关注,第一时间接收最新文章。

目录
相关文章
|
2月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
1月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
1月前
|
Java 测试技术 数据库
Spring事务传播机制(最全示例)
在使用Spring框架进行开发时,`service`层的方法通常带有事务。本文详细探讨了Spring事务在多个方法间的传播机制,主要包括7种传播类型:`REQUIRED`、`SUPPORTS`、`MANDATORY`、`REQUIRES_NEW`、`NOT_SUPPORTED`、`NEVER` 和 `NESTED`。通过示例代码和数据库插入测试,逐一展示了每种类型的运作方式。例如,`REQUIRED`表示如果当前存在事务则加入该事务,否则创建新事务;`SUPPORTS`表示如果当前存在事务则加入,否则以非事务方式执行;`MANDATORY`表示必须在现有事务中运行,否则抛出异常;
73 4
Spring事务传播机制(最全示例)
|
16天前
|
Java 关系型数据库 MySQL
Spring事务失效,我总结了这7个主要原因
本文详细探讨了Spring事务在日常开发中常见的七个失效原因,包括数据库不支持事务、类不受Spring管理、事务方法非public、异常被捕获、`rollbackFor`属性配置错误、方法内部调用事务方法及事务传播属性使用不当。通过具体示例和源码分析,帮助开发者更好地理解和应用Spring事务机制,避免线上事故。适合所有使用Spring进行业务开发的工程师参考。
18 2
|
17天前
|
Java 程序员 Spring
Spring事务的1道面试题
每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。
Spring事务的1道面试题
|
28天前
|
Java Spring
Spring 事务传播机制是什么?
Spring 事务传播机制是什么?
19 4
|
19天前
|
监控 Java 数据库
Spring事务中的@Transactional注解剖析
通过上述分析,可以看到 `@Transactional`注解在Spring框架中扮演着关键角色,它简化了事务管理的复杂度,让开发者能够更加专注于业务逻辑本身。合理运用并理解其背后的机制,对于构建稳定、高效的Java企业应用至关重要。
31 0
|
2月前
|
Java 程序员 数据库连接
女朋友不懂Spring事务原理,今天给她讲清楚了!
该文章讲述了如何解释Spring事务管理的基本原理,特别是针对女朋友在面试中遇到的问题。文章首先通过一个简单的例子引入了传统事务处理的方式,然后详细讨论了Spring事务管理的实现机制。
女朋友不懂Spring事务原理,今天给她讲清楚了!
|
2月前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
2月前
|
前端开发 Java 数据库连接
一天十道Java面试题----第五天(spring的事务传播机制------>mybatis的优缺点)
这篇文章总结了Java面试中的十个问题,包括Spring事务传播机制、Spring事务失效条件、Bean自动装配方式、Spring、Spring MVC和Spring Boot的区别、Spring MVC的工作流程和主要组件、Spring Boot的自动配置原理和Starter概念、嵌入式服务器的使用原因,以及MyBatis的优缺点。