JAVA状态机—COLA-statemachine源码解析

写在前面

​ 回顾今年做的比较有代表性的一些功能点,我想手搓了一部分流程引擎应该排得上号。但回忆起当时的代码书写过程,不能说苦不堪言,也得说叫苦不迭。我负责的是单据拆分和合并以及后续流转的功能,按理说拆分或者合并后走原有的流程引擎就可以。但苦就苦在这部分功能涉及到两平台数据交互,拆单或者合单是你B平台的事,关我A平台什么事,所以一旦涉及到某些关键状态的变更,就会扯出一堆处理逻辑,要么等位延迟之类的。

​ 后来功能上线后,某次偶然的机会才在某处看到一篇介绍Spring Statemachine的文章,深感优雅,后续想进一步学习时,实则又发现略显臃肿。后续经过一些了解,发现也有一些简易好使的状态机。本文介绍的就是其中之一,COLA-statemachine。

简要介绍

​ 关于COLA-statemachine的溯源我不做过多说明了。可以参考其作者自己写的介绍文章https://blog.csdn.net/significantfrank/article/details/104996419,注意其中的github地址已失效,相关组件已经全部被作者移动到component组件文件夹下。

​ 简单说明一下为什么在这种情况下觉得COLA状态机好使而不是Spring状态机好使。首先Spring状态机是Spring生态下的产物,我写流程插件的项目所使用的并非Spring框架。其次,COLA的作者有句话我很认同,不管是Spring状态机还是松鼠状态机,他们的优点是功能很完备,缺点也是功能很完备。我不需要如此复杂的功能,只需要对流程流转能进行正常的状态管理即可。此外,简单小巧的工具,也更加方便我后续能够进行改造,比如我看到过的草帽状态机,就是基于COLA自定义开发的。最后,Spring状态机本身是有状态的,既然本身有状态,那么在高并发状态下,势必要加锁,增加额外的开销,影响性能。而COLA本身无状态,它就像一条无情的流水线,给它一个状态,处理完返回一个状态(可能是原状态)。

COLA状态机源码

先理解状态模型

在源码之前,我们先看两个东西,一是这张图。

在这里插入图片描述

我们看看图中文本的释义:

在这里插入图片描述

用我自己的话来说就是某个事件驱使状态由A流转到B。而流转又分为内部流转和外部流转,内部流转没有状态改变,但是会触发一系列动作。而状态是否改变,动作是否实施,都要进行条件判断。如此一来,上图就已经解释清楚了。

如果对状态机是要做什么仍然模糊,不妨看看uml提供的简单水相模型。https://www.uml-diagrams.org/examples/water-phase-uml-state-machine-diagram-example.html

在这里插入图片描述

这是状态模型里面我个人认为最简单的模型了,其他银行模型,支付模型之类的就不展示了。归结为一句话,就是水在不同动作下变为不同形态(固液气,离子)。

再看COLA状态机的效果
无分支

在这里插入图片描述

作者共提供了四个测试类,只看其中两种。第二种和第四种会穿插说到。

作者的断言写的很好,没问题。但这里部分代码的改动只是为了更直观。

在这里插入图片描述

直接看执行效果

在这里插入图片描述

输出结果第一行是上下文,作用是在进行条件判断,第二行是判断通过,状态机状态改变时的执行动作(下同)。

在这里插入图片描述

在这里插入图片描述

这里个测试就是作者写了个异常处理,没什么好说的,错误原因也说明了,在定义状态机的时候,是通过事件1(EVENT1)把状态1变为状态2,而这里是想通过事件1,对状态2的状态进行更改,自然报错。

至于为什么,这里可以提前看一部分源码。

在这里插入图片描述

20231208192531548.png&pos_id=img-T3iFfzho-1702822992462)

在这里插入图片描述

在这里插入图片描述

简明扼要的说就是状态机在初始化的时候,就将当前状态机对应事件存起来了,这里去拿想要流转到目标状态所需要执行事件时没拿到。

在这里插入图片描述

测试状态机的判断方法,状态机定义与上面相同,这里是判断该状态能否通过该事件改变状态。

接下来直接看这个带有一定逻辑状态的状态机。

在这里插入图片描述

在这里插入图片描述

这里我直接将它的uml图打出来了,在idea安装插件后可直接查看。

在这里插入图片描述

去掉无关紧要的输出,我们直接看多线程异步调用的情况下该状态机的表现。如何证明它是线程安全的呢,严格来说,状态3,状态4都为最终态,也就是说,在多线程异步调用情况下,所有最终状态都是4,而没有输入状态是4,那我们就可以认为它保证了最基本的原子性。

在这里插入图片描述

执行了90次,状态机未报错,输入状态也无3,4其中之一。

有分支

在这里插入图片描述

看效果即可,根据不同判断同一事件输出不同结果状态。

在这里插入图片描述

在这里插入图片描述

源码解析

​ 其实上面两个测试类一说完,基本就囊括了COLA状态机这个基础的状态机,不过还是把源码过一遍,增强代码阅读能力。

在这里插入图片描述

在这里插入图片描述

这是截止2023年12月份的最新代码结构。挑关键的,带有逻辑的看。我们回到状态机定义的例子。

在这里插入图片描述

定义一个状态机的要素就在这里。值得一提的是,作者是使用Fluent Interface来实现状态机,Fluent API极大程度上保证了构建器的调用顺序,而实现方式就是每一个抽象类只定义下一步要调用的方法。

状态机构建工厂

在这里插入图片描述

将构建接口和实现类都看一下。

在这里插入图片描述

在这里插入图片描述

再一看下状态机的实现类。

在这里插入图片描述

这里的方法主要是实现了状态机的一些预检,校验,异常操作,获取流转方法等。那么流转方法是从哪里来的呢?让我们回到构建器的实现类

在这里插入图片描述

我们看看流转构建器实现了什么?

在这里插入图片描述

事实上流转构建器实现了内部/外部的流转构建器接口。

在这里插入图片描述

简言之,根据stateid对SEC(状态,事件,上下文)做了定义,相当于根据状态ID,将与之相关的事件,上下文做了绑定。

在这里插入图片描述

在查看这个获取状态的方法时,还发现了源代码的逻辑bug。

在这里插入图片描述

在这里插入图片描述

作者采取的是没有就新建的方式,因此后面获取的时候逻辑上是不会为null的,有缘者可以去github提合并申请。

回过头来,我们已经看到流转方法构建器这一步了(其他两个流转方法构建器类似)。流转方法定义完成,和状态id一起存起来,基本就完成了。这里再看一个前面说到的顺序强制的实现逻辑。

在这里插入图片描述

流转方法构建器继承AbstractTransitionBuilder,抽象的AbstractTransitionBuilder又实现了form,on,to类。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这样的定义顺序,就实现了调用顺序的强制要求。这里需要注意一下的是,我前面只说了一种流转方法构建类,但其他两种是类似的,不做过多说明。

在这里插入图片描述

在这里插入图片描述

抽象类里一样只有form方法。

写在最后

​ COLA状态机源码实际上并不复杂,稍加看看就能看懂,给出的测试用例也很详实,感兴趣的可以把源码拉下来看看用用。原本我是想手搓一个状态机的,但用脚趾头想也知道没COLA写的好,所以算了。另外我还是那个观点,单靠一篇文章就看懂某个东西比较困难,大家行文的侧重点都不一样,建议交叉着,配合着看。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值