
5.5 扩展非黑名单元素
上文提到过只有process、userTask、definitions元素解析的时候,Activiti才会使用黑名单机制对自定义属性进行解析和存储,看到这里可能会有疑问,除了上面所说的三种元素,Activiti是否也对其他元素的扩展属性进行了处理?有点遗憾,Activiti并没有对其他元素的扩展属性进行处理,如果开发者期望在其他元素中使用自定义属性,该如何操作?
5.5.1 自定义元素解析器
这里以开始节点(StartEvent元素)的自定义属性解析为例,详细讲解开始节点的自定义属性解析过程,为了简单起见,直接自定义开始节点解析器,并继承StartEventXMLConverter(开始节点的默认解析器),相关实现如代码清单5-8所示。
代码清单5-8 ShareniuStartEventXMLConverter.java

在上述代码中,第2~3行初始化了黑名单集合defaultElementAttributes,该集合定义了id、name两个属性,这样引擎解析开始节点时,除了以上定义的两个属性之外,其他属性都会作为扩展属性进行处理,第4~6行告诉引擎开始节点的属性信息使用StartEvent类进行封装,第7~9行告诉引擎程序要解析startEvent类型的元素,第10行中定义的convertXMLToElement方法负责解析开始节点,该过程比较复杂,其处理逻辑如下。
(1)解析常规属性。
第11行解析常规属性,例如formKey。第13~21行根据formKey属性值的有无,实例化不同的类,第22行解析并存储开始节点在流程文档中的坐标信息,第23~24行设置initiator属性值,initiator属性的配置形如activiti:initiator="shareniu",这样启动流程实例时,该属性会作为流程实例级别的变量存在。该属性的处理可以参考13.3节。
(2)解析自定义属性。
第26行调用BpmnXMLUtil类中的静态方法addCustomAttributes进行自定义属性的解析工作。
(3)解析子元素。
第27行调用parseChildElements方法进行子元素的解析工作。
可能阅读完上面的代码会有疑惑,上述自定义开始节点的解析器就是直接将StartEventXMLConverter类中的解析代码复制过来然后添加了第26行代码,为何不直接调用父类的方法,如代码清单5-9所示。
代码清单5-9 ShareniuStartEventXMLConverter.java

在上述代码中,convertXMLToElement方法的处理逻辑为:第2行委托父类的convertXMLToElement方法进行元素解析工作并返回解析结果element;第3行实现自定义属性的解析。
上面的代码处理逻辑看起来很合理,也很清晰,但是这样操作是完全错误的,该案例中convertXMLToElement方法主要用于解析开始节点,第4章中讲解了流模型解析文档的原理,使用流模型解析文档时读取到的数据流只能前进不能回退,如果第2行委托父类进行元素的解析工作则势必会涉及子元素的解析,如果执行完子元素的解析工作之后,再次执行第3行解析父类的属性,这样的操作是完全错误,因为流模型解析文档的时候数据流只能前进不能回退。
该案例也从侧面说明一个问题,了解原理方能看透本质,才能更好地运用框架提供的功能。
5.5.2 替换引擎元素解析器
接下来使用自定义开始节点解析器以替换开始节点的默认解析器StartEvent-XMLConverter,相关实现如代码清单5-10所示。
代码清单5-10 App.java和startEvent.bpmn

在上述代码中,第2~11行定义了流程文档startEvent.bpmn,其中第3行为开始节点定义了shareniu:id、shareniu:name两个扩展属性。第22行使用自定义开始节点解析器,替换引擎默认实现,该步骤非常重要。执行上述代码,控制台打印的信息如下:
id, shareniuId name, shareniuName
本节主要讲解了自定义元素、自定义任务节点的属性以及自定义元素解析器的处理过程,到此为止Activiti中所有的元素以及元素的解析工作已经讲解完毕。
扩展
其他元素扩展属性的实现过程可以结合该案例进行深入学习。