在实现自定义节点缓存类之前我们首先思考如下几个问题。
- 节点缓存类注入引擎。
可能读者会想,这还不简单,直接通过设置ProcessEngineConfigurationImpl 实例对象中的流程定义信息缓存processDefinitionInfoCache值即可,不就完成属性注入工作了吗?但是很遗憾,Activiti在这里给我们开了一个小玩笑,在ProcessEngineConfigurationImpl 类中我们并没有发现任何设置processDefinitionInfoCache属性值的函数。这个时候,我们该怎么办呢?既然流程引擎配置类没有提供setter方法,我们可以自定义一个类并继承默认的引擎配置类ProcessEngineConfigurationImpl从而完成对父类processDefinitionInfoCache属性值进行获取或者赋值操作。
- 命令执行器注入自定义类。
上面我们了解到ProcessDefinitionInfoCache类的构造函数中需要一个输入参数,而且该参数的类型为CommandExecutor。因此自定义类继承ProcessDefinitionInfoCache类的同时,必须显式调用其父类的构造方法,因此为了确保父类不被污染,能继续独立运行,我们务必要想尽一切办法获取到CommandExecutor实例对象并传递给父类,形如public MyProcessDefinitionInfoCache(CommandExecutor ce) {super(ce);}。那么问题来了,如果我们使用Spring进行bean的实例化工作,CommandExecutor实例对象是流程引擎配置类中的对象,我们怎么能够获取到该对象呢?因为CommandExecutor类的实例化工作完全由引擎内部进行实现,很显然,通过配置文件方式将该自定义类注入到ProcessEngineConfigurationImpl类中有点不大可能实现,既然该方式不太容易实现,我们不妨换一个思路,之前我们学习过Activiti配置器,相信读者还有一定的印象,在此不妨一试,毕竟CommandExecutor实例对象可以通过引擎实例对象直接获取到。
- ProcessDefinitionInfoCacheObject类。
因为ProcessDefinitionInfoCache类默认将流程节点定义信息封装为ProcessDefinitionInfoCacheObject类的实例进行操作,ProcessDefinitionInfoCacheObject实例封装了流程定义id,节点版本以及节点信息对应的ObjectNode对象。形如{"bpmn":{"usertask1":{"userTaskName":"shareniu"}}}。由于我们需要使用Redis对节点定义信息进行缓存,因此需要缓存的对象必须实现java中的Serializable接口,Activiti在定义ProcessDefinitionInfoCacheObject类的时候又给我们开了一个天大的玩笑,该类并没有实现Serializable接口,因此我们使用Redis存储该类的实例对象肯定会报错,所以我们在这里需要自定义一个缓存数据存储类,并可以将自定义类的实例与ProcessDefinitionInfoCacheObject类的实例进行相互转换。
这里要吐槽一下,笔者觉得这个节点缓存类的设计完全不符合Activiti的一贯风格,如果读者一直跟随笔者的分析思路到这里,相信读者或多或少对Activiti的编码风格以及设计思想有所了解,Activiti的一贯做法就是将功能抽象为接口并提供默认实现类,在节点缓存处理类中Activiti并没有将其设计为接口,反倒只是一个普通的类,而且需要缓存的对象并没有实现序列化接口,如果我们需要自定义节点缓存存储类则必须继承ProcessDefinitionInfoCache类,但是该类中并没有定义无参构造函数,因此子类必须在自身的构造函数中指定命令执行器commandExecutor,这样的设计大大增加了客户端操作的复杂度,实现自定义节点缓存类,对我们来说压根就是一次代码重写。通过processDefinitionInfoCache属性将自定义的类注入ProcessEngineConfigurationImpl类的时候,引擎并没有提供任何设置processDefinitionInfoCache属性的函数。上面所陈述的一系列问题都需要我们自己想办法去解决。所幸,我们是一本Activiti源码解析的书籍,对于commandExecutor对象的获取以及属性动态注入的引擎机制还是比较了解的。
既然上面的一系列问题Activiti并没有提供解决方案,我们何不通过扩展源码的方式进行相应的实现,可能读者会想,能不能使用修改源码的方式进行上述问题的解决,笔者认为扩展源码的方案比直接修改源码的方式要优雅一点,因为如果我们直接修改源码并对Activiti中不合理的地方进行改造,则Activiti框架需要升级的时候,修改的代码需要迁移到新版本,这个工作量也不小,而扩展源码则不会出现类似这样的问题。
技术团队支持:盘古BPM工作流平台
具体效果参考盘古BPM