上一篇博文分析了对象解析器的原理。本文来编写一个自定义对象解析器。考虑这样的场景:生产中我们常常在流程的上一环节选择下环节的处理人,然后再提交流程。之后流程运转到下一个环节后,会在对应的处理人名下,即userTask的candidate或assignee是上环节选中的处理人。
上述的实现方式可以是这样,对userTask添加自定义任务监听器,该监听器主要作用是把流程实例中的变量设置为userTask的candidate或assignee。这样在流程执行的时候,上一环节选择了处理人,写入流程变量中,待下一环节的任务监听器把变量读出来设置到userTask的candidate或assignee。但是在bpmn图上为每个userTask上添加这样的监听器,随着流程和userTask增多,维护起来非常繁琐。生产系统常常有十几个不同的流程,每个流程数个或十数个节点,而且随着业务变化,流程图也要时不时进行修改,手动对每个userTask添加监听器不现实。有没有办法为任务节点统一添加监听器呢?答案是有的!我们可以自定义一个userTask的对象解析器,在解析每个userTask时,为其添加监听器,这样我们就没必要在bpmn图上手动添加了。不仅省时省力,而且方便统一管理。
首先我们自定义一个监听器AssignmentTaskListener.java
public class AssignmentTaskListener implements TaskListener{
public void notify(DelegateTask delegateTask) {
String assignee = (String)delegateTask.getVariable("assignee");
delegateTask.setAssignee(assignee);
}
}
改监听器主要作用就是在触发的时候,把流程变量"assignee"中的值设置到userTask的assignee中。
接着我们扩展userTask的解析器,自定义MyUserTaskParseHandler.java
public class MyUserTaskParseHandler extends UserTaskParseHandler{
private List taskListenerList;
@Override
protected void executeParse(BpmnParse bpmnParse, UserTask userTask) {
//由父类完成元素解析
super.executeParse(bpmnParse, userTask);
for(TaskListener taskListener : taskListenerList) {
TaskDefinition taskDefinition = (TaskDefinition)bpmnParse.getCurrentActivity().getProperty(UserTaskParseHandler.PROPERTY_TASK_DEFINITION);
taskDefinition.addTaskListener(TaskListener.EVENTNAME_CREATE, taskListener);
}
}
public List getTaskListenerList() {
return taskListenerList;
}
public void setTaskListenerList(List taskListenerList) {
this.taskListenerList = taskListenerList;
}
}
第8行先调用父类进行元素解析。10-12行对当前userTask添加create事件的任务监听器。为何不在assignment事件触发时添加任务监听器呢?因为如果bpmn图中userTask没有设置assignee的话,流程引擎就不会触发assignment事件,自然就不会调用任务监听器了。
接着我们新建一个流程引擎配置文件,命名为activitiParseHandler.cfg.xml:
17-27为自定义对象解析器。在初始化流程引擎时,对象解析器通过map方式管理,自定义添加的对象解析器会替换getHandledType()返回值相同的默认对象解析器。MyUserTaskParseHandler继承了UserTaskParseHandler,getHandledType()返回的都是UserTask.class,因此前者会替换后者。20-24行通过xml注入监听器,增加配置灵活度。
然后我们部署流程:
private ProcessEngine pe;
public void getFromProcessEngineConfiguration() {
ProcessEngineConfiguration pec = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource("activitiParseHandler.cfg.xml");
pe = pec.buildProcessEngine();
}
public void deploy() {
RepositoryService repositoryService = pe.getRepositoryService();
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
InputStream inputStream = null;
try {
inputStream = App.class.getClassLoader().getResource("bpmn/parseHandlerBPM.bpmn").openStream();
deploymentBuilder.addInputStream("parseHandler.bpmn", inputStream);
deploymentBuilder.name("parseHandlerDeployment");
Deployment deployment = deploymentBuilder.deploy();
System.out.println("流程部署ID:" + deployment.getId());
System.out.println("流程部署名:" + deployment.getName());
} catch (IOException e) {
e.printStackTrace();
}
}
查看act_ru_procdef表:
接下来我们启动parseHandlerBPM:1:4流程,启动时加入流程变量:
public void startProcessById() {
RuntimeService runtimeService = pe.getRuntimeService();
Map var = new HashMap();
var.put("assignee", "张三");
ProcessInstance pi = runtimeService.startProcessInstanceById("parseHandlerBPM:1:4", var);
}
启动成功后,此时流程应该运行到userTask1节点上,观察act_ru_task:
ASSIGNEE属性值为“张三”,证明自定义对象解析器成功为userTask1添加监听器,并且监听器根据流程变量为userTask1设置了ASSIGNEE。接下来尝试把流程提交到userTask2节点:
public void completeTask() {
TaskService taskService = pe.getTaskService();
Map var = new HashMap();
var.put("assignee", "李四");
taskService.complete("2505", var);
}
提交完成后,观察act_ru_task:
通过截图看到“李四”也成功设置为userTask2的ASSIGNEE。
在企业流程应用中,设置环节处理人的方法很多,上述仅仅是一个示例。除了把下环节处理人写到流程变量,也可以写到数据库表中,或者通过其他方式去实现,这里不再细述。使用自定义对象解析器,我们除了可以实现本文提到的为节点添加监听器的功能外,还可以实现自定义扩展属性的读取。例如在bpmn图中,我们为某个元素添加了自定义扩展属性。在解析元素时,读取这些自定义扩展属性,可以扩展元素的功能。