发现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技术],欢迎关注,第一时间接收最新文章。

目录
相关文章
|
20天前
|
Java 数据库 开发者
|
5天前
|
SQL Java 关系型数据库
Spring 事务
Spring 事务
8 1
|
7天前
|
Java 数据库连接 数据库
Spring事务简介,事务角色,事务属性
Spring事务简介,事务角色,事务属性
17 2
|
11天前
|
Java 数据库连接 数据库
16:事务-Java Spring
16:事务-Java Spring
27 5
|
13天前
|
消息中间件 Java 关系型数据库
Spring事务与分布式事务
这篇文档介绍了事务的概念和数据库事务的ACID特性:原子性、一致性、隔离性和持久性。在并发环境下,事务可能出现更新丢失、脏读和不可重复读等问题,这些问题通过设置事务隔离级别(如读未提交、读已提交、可重复读和序列化)来解决。Spring事务传播行为有七种模式,影响嵌套事务的执行方式。`@Transactional`注解用于管理事务,其属性包括传播行为、隔离级别、超时和只读等。最后提到了分布式事务,分为跨库和跨服务两种情况,跨服务的分布式事务通常通过最终一致性策略,如消息队列实现。
|
14天前
|
监控 Java 测试技术
Spring Boot与事务钩子函数:概念与实战
【4月更文挑战第29天】在复杂的业务逻辑中,事务管理是确保数据一致性和完整性的关键。Spring Boot提供了强大的事务管理机制,其中事务钩子函数(Transaction Hooks)允许开发者在事务的不同阶段插入自定义逻辑。本篇博客将详细探讨事务钩子函数的概念及其在Spring Boot中的应用。
36 1
|
18天前
|
XML Java 数据库连接
精妙绝伦:玩转Spring事务编程的艺术
【4月更文挑战第20天】
31 0
|
25天前
|
XML Java 数据库
在Spring框架中,XML配置事务
在Spring框架中,XML配置事务
|
25天前
|
Java 数据库连接 数据库
spring事务失效(疑难杂症)
spring事务失效(疑难杂症)
|
1月前
|
XML Java 关系型数据库
注解驱动事务:Spring中基于注解的事务属性配置详解
注解驱动事务:Spring中基于注解的事务属性配置详解
43 0
注解驱动事务:Spring中基于注解的事务属性配置详解