最近用到了flowable工作流,由于项目需要在工作流模板中添加自定义属性,因此查了资料踩了坑,花了几天时间研究,在这儿总结下。
网上有关flowable添加自定义属性的文章不少,但是很多写的不够“小白”,我会尽量详细的介绍实操的整个流程。
首先说明这里设置的自定义属性是针对工作流模板的,即属性会在模板里定义好并赋值。请和工作流实例化后的变量区分开。
先看一下flowable原生的属性有哪些:打开flowable的UI界面,任意创建一个模板,点击任意一个节点,可以在下方看到原生属性,如id,Documentation等等(蓝色框里的属性是我添加的自定义属性,若还未添加是看不到的)。
flowable原生的属性就这么多,要想DIY那就必须改源码!
请小伙伴们不要看到改源码就头疼,因为网上有很多大神已经把路铺好了,我也只是一个大神后面的小白,所谓前人栽树后人乘凉,真正需要修改的地方其实很少,而且一看就懂!
目录
一、下载源码
要改源码就得先有源码(废话--\\\),flowable是开源的,因此小伙伴们可以直接到flowable官网下载源码↓↓↓
这里请注意,建议下载自己项目当前使用的flowable版本的源码,如你项目中在用flowable6.4.0,那你就下载6.4.0版本的源码,避免改了一大圈后面发现版本不兼容想砸脑壳的冲动。另外,flowable的源码是用github管理的,下载速度超级慢!!!中间非常容易断,因此强烈建议从国内的Gitee下载!
下载源码之后,用IDE创建项目,我这里用的IDEA↓↓↓
创建之后经过漫~长~的等待import之后,项目就初始化完毕啦~~~
打开modules这个文件夹,里面就是flowable的源码了。这个时候可能会出现有些文件夹未自动识别成source的情况,即文件夹右下角没有蓝色方块↓↓↓
可能是IDE太笨......如果没有识别到source,你会发现里面的代码都是“清一色”,不会自动显示错误,也无法找到方法定义等↓↓↓
别慌,因为我们要改的源码只存在两个包里,因此只要确保以下两个包有正确识别即可:
那么如果未正确识别怎么办呢?手动添加即可:
到这里源码就准备完成了。
二、修改stencilset_bpmn.json文件
简单来说(其实是我也不太清楚)stencilset_bpmn.json保存了所有工作流模板中的节点信息,包括各个节点的属性。工作流UI界面上能看到的节点及节点下的属性都是从这个json文件读取的,因此首先需要修改这个文件,把自定义属性加进来,这样你在界面上才能看到自定义的属性,也能够让项目中的其他人员编辑。
stencilset_bpmn.json文件位于\flowable-engine\modules\flowable-ui-modeler\flowable-ui-modeler-logic\src\main\resources中。找到该文件并打开(我这里用的notePad)↓↓↓
首先在propertyPackages中添加自定义的属性:
然后找到你要把这个自定义属性添加到的那一类节点上。我这里是添加到userTask节点上,因此找到userTask的定义,并把自定义属性加进去↓↓↓
保存,结束。是不是很简单?
三、识别自定义属性
在stencilset_bpmn.json文件里添加自定义属性后,如果此时重新打包部署,在flowable-UI界面上就能够看到自定义属性啦↓↓↓
然而先别高兴太早,这个时候只是界面上能看到,flowable此时是不识别该自定义属性的!如果不信,可以下载该模板,然后打开,会发现在bpmn文件中找不到自定义的属性↓↓↓
因为flowable还没有真正认可你这个自定义属性。那接下来就是让flowable识别该属性了。
找到UserTaskJsonConverter.java文件(位于flowable-engine\modules\flowable-json-converter\src\main\java\org\flowable\editor\language\json\converter)。
接下来需要修改两个方法convertJsonToElement和convertElementToJson。从名称上也可以看出来,这两个文件就是用来转换bpmn元素(element)的(从json到element和从element到json)。其实你从flowable-UI上下载模板的时候,flowable就是用这两个方法将你界面上的东西转换成bpmn文件的。我们需要修改这两个方法,让flowable识别自定义属性。
先看convertElementToJson方法。直接在该方法的末尾添加以下内容:
List<ExtensionElement> customNodeTypeElements = userTask.getExtensionElements().get(PROPERTY_USERTASK_CUSTOMNODETYPE);
if (CollectionUtils.isNotEmpty(customNodeTypeElements)) {
setPropertyValue(
PROPERTY_USERTASK_CUSTOMNODETYPE,
userTask.getExtensionElements().
get(PROPERTY_USERTASK_CUSTOMNODETYPE).
get(0).
getElementText(),
propertiesNode);
} else {
String attrValue = "";
Map<String, List<ExtensionAttribute>> attributeMap = userTask.getAttributes();
if (MapUtils.isNotEmpty(attributeMap)) {
List<ExtensionAttribute> values = attributeMap.get(PROPERTY_USERTASK_CUSTOMNODETYPE);
if (CollectionUtils.isNotEmpty(values)) {
attrValue = values.get(0).getValue();
}
}
setPropertyValue(PROPERTY_USERTASK_CUSTOMNODETYPE, attrValue, propertiesNode);
}
这里的PROPERTY_USERTASK_CUSTOMNODETYPE是自定义的属性id,你可以直接在这个类的顶部定义,也可以和其他变量写在一起:
这里需要特别注意,该变量的值必须和你在stencilset_bpmn.json里添加的自定义属性的id完全一样,不然无法识别!另外这里有个坑,flowable这里是不识别驼峰命名的,因此要不就全小写,要不就像我这样用_连接。如果你在json文件里定义了customNodeType,然后这里也写成customNodeType,flowable是不会识别的!
下面看convertJsonToElement这个方法,先添加这一段代码:
List<CustomProperty> customProperties = new ArrayList<>();
//自定义属性
String customNodeType = getPropertyValueAsString(PROPERTY_USERTASK_CUSTOMNODETYPE, elementNode);
if (StringUtils.isNotBlank(customNodeType)) {
CustomProperty customProperty = this.createPropery(PROPERTY_USERTASK_CUSTOMNODETYPE, customNodeType);
customProperties.add(customProperty);
}
//设置自定义属性到usertask
if (CollectionUtils.isNotEmpty(customProperties)) {
task.setCustomProperties(customProperties);
}
简单说下这段代码的意义。首先,根据自定义属性的id去elementNode(elementNode对象会记录模板定义中的所有信息,包括你的自定义节点属性)中找到自定义属性的值 (这就是为什么自定义属性id要与stencilset_bpmn.json文件中添加的一致)。之后新建一个CustomProperty,然后把这个customProperty加到userTask的customProperties中去就可以了。没错,userTask有个属性就叫做customProperties!还是比较贴心的。(这里也可以看出来flowable原生就是支持自定义属性的,还专门留了类给开发者定制化)
这里用到了一个私有方法,因此在该类里还得定义这样一个私有方法:
private CustomProperty createPropery(String propertyName, String propertyValue) {
CustomProperty customProperty = new CustomProperty();
customProperty.setId(propertyName);
customProperty.setName(propertyName);
customProperty.setSimpleValue(propertyValue);
return customProperty;
}
好,这里就是所有要添加的代码了!
四、打包与部署
其实到第三步基本就完成了,我在网上查到的大多数资料也都是到第三步。但是对于我这种小白来说还要再进一步,怎么把修改后的源码重新打包呢?
在打包之前,请先查看下当前项目连接的maven库是哪个:
找到你的maven库,并备份以下两个jar包:
flowable-json-converter-6.4.0.jar和flowable-ui-modeler-logic-6.4.0.jar。
接下来就是重新编译打包了。
在IDEA的maven窗口找到这两个source(前提是已正确识别,不然请跳到第一步)↓↓↓
打开Lifecycle并双击package ,之后IDEA会自动开始编译该模块。注意这里使用的是package,而非install或deploy。后两种打包方式会直接替换你本地的maven库中的同名jar包。
然后再看左侧目录树,在target下就会有新生成的jar包了↓↓↓
到这儿就完成了源码的修改和重编译。我们得到了两个修改后的jar包。那么怎么集成这两个jar包到自己的项目里呢?
打开自己的项目,检查下maven的引用目录,然后找到该目录下的两个同名jar包,备份后,把新的jar包拷贝到相同位置,之后将项目重新打包,这样就将修改源码后的jar包集成到我们自己的项目中了。
再进一步,如果你的代码是托管的,部署也是自动化的,例如使用gitlab和jenkins,怎么打包呢?总不能每次都用上面的方法在本地打包吧。
因此你需要将这两个jar包放在自己的项目中,并且设置项目读这两个jar包。
我的项目中common目录是公用的,在resource下新建了lib目录,并把jar包放在这儿,这样整个项目都可以读到这个jar包。
之后修改pom文件↓↓↓
<dependency>
<groupId>flowable-json-converter</groupId>
<artifactId>flowable-json-converter</artifactId>
<version>6.4.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/flowable-json-converter-6.4.0.jar</systemPath>
</dependency>
把以上修改都推到git上即可。
那么到这里有关flowable自定义属性的添加就完成了。
五、在项目中获取自定义属性
我们已经把自定义属性添加到项目的工作流中了,那么怎么在实际运行中获取到这个自定义属性呢?
首先根据流程定义id和任务定义id可以找到某个流程的某个节点,并获取该节点的extensionElements。customProperties其实就存放在extensionElements里面,包括我们自定义的节点属性。
之后就可以根据自定义节点的id获取自定义节点的值↓↓↓