任务是流程中最重要的组成部分。Flowable提供了多种任务类型,以满足实际需求。
常用任务类型有:
-
用户任务
-
Java Service任务
-
脚本任务
-
业务规则任务
-
执行监听器
-
任务监听器
-
多实例
集成扩展的任务类型有:
-
手动任务
-
Java接收任务
-
Shell任务
-
补偿处理器
-
Web Service任务
-
邮件任务
-
Http任务
-
Camel任务
-
Mule任务
任务的图形都是以一个圆角矩形为基础,在左上角添加具体类型的图标。
一、常用的任务类型
1.1 用户任务
1.1.1 描述
“用户任务(user task)”指需要人工执行的任务。当流程执行到达用户任务时,流程实例会停止等待,直到用户触发完成任务动作。
1.1.2 图示
用户任务用左上角有一个小用户图标的标准任务(圆角矩形)表示。
1.1.3 XML表示
用户任务在XML中如下定义。其中id是必须属性,name是可选属性。
<userTask id="theTask" name="重要任务" />
1.1.4 到期日期
每个任务都可以设置到期日期(due date)。
可以指定固定时间或相对时间,比如,当dueDate为“PT30M”时,表示到达任务30分钟后到期。
到期日期必须符合java.util.Date或java.util.String(ISO8601格式)。
实际应用,我们指定为变量值。
<userTask id="theTask" name="Important task" flowable:dueDate="${dateVariable}"/>
任务的到期日期可以使用TaskService,或者在TaskListener中使用传递的DelegateTask修改。
1.1.5 任务指派
- 指派确定的办理人
<userTask id="theTask" name="重要任务" flowable:assignee="jinyangjie"/>
- 指派潜在办理人
<userTask id="theTask" name="重要任务" flowable:candidateUsers="jinyangjie, zhangsan" />
- 指派潜在办理组
<userTask id="theTask" name="重要任务" flowable:candidateGroups="leader, manager" />
更多任务指派的内容,已在“用户和组”的篇章中介绍,这里不再赘述。
1.2 Java Service任务
1.2.1 描述
Java Service任务(Java service task)用于调用Java类。Java Service不属于BPMN2.0规范,而是Flowable的自定义扩展。
1.2.2 图示
服务任务用左上角有一个小齿轮图标的圆角矩形表示。
1.2.3 XML表示
有三种方法声明如何调用Java逻辑,下面分别介绍:
- 调用固定的类
使用flowable:class属性提供全限定类名(fully qualified classname),指定流程执行时调用的类,该类必须实现JavaDelegate或ActivityBehavior接口。
<serviceTask id="javaService" flowable:class="com.example.service.MyJavaDelegate" />
- 调用动态类
使用flowable:delegateExpression属性提供委托对象(delegation object)的表达式。该功能和flowable:class类似,同样需要实现JavaDelegate或ActivityBehavior接口,只不过这里不是指定一个具体的实现类,而是查询指定名称的Bean对象。
<serviceTask id="javaService" flowable:delegateExpression="${myDelegateExpressionBean}" />
myDelegateExpressionBean
是一个实现了JavaDelegate
接口的bean,定义在Spring容器中。
- 调用类的指定方法或属性值
使用flowable:expression属性指定类的方法或属性值。同样的,该类需要实现JavaDelegate或ActivityBehavior接口。
<serviceTask id="javaService" flowable:expression="#{printer.printMessage()}" />
将在名为printer
的对象上调用printMessage
方法(不带参数)。当然也可以为表达式中使用的方法传递变量。
属性值示例:
<serviceTask id="javaService" flowable:expression="#{printer.ready}" />
会调用名为printer
的bean的ready
参数的getter方法,getReady
(不带参数)。该值会被解析为执行的流程变量。
1.2.4 具体实现实例
下面是一个Java类的示例,用于将流程变量String改为大写。这个类通过实现org.flowable.engine.delegate.JavaDelegate接口,可以在流程执行中被调用。
同时,需要重写*execute(DelegateExecution)*方法实现业务逻辑。这个方法就是引擎将调用的方法。另外,通过该方法中的DelegateExecution参数可以访问流程实例的各种信息。
public class ToUppercase implements JavaDelegate {
public void execute(DelegateExecution execution) {
String var = (String) execution.getVariable("input");
var = var.toUpperCase();
execution.setVariable("input", var);
}
}
如果实现org.flowable.engine.impl.delegate.ActivityBehavior接口,可以访问更强大的引擎功能,例如,可以影响流程的控制流程。但注意这并不是好的实践,需要避免这么使用。
1.2.5 任务的返回值
服务执行的返回值(仅对使用表达式的服务任务),可以通过为服务任务定义的*‘flowable:resultVariable’*属性设置为流程变量。可以是已经存在的,或者新的流程变量。 如果指定为已存在的流程变量,则流程变量的值会被服务执行的返回值覆盖。 如果不指定结果变量名,则服务任务的返回值将被忽略。
<serviceTask id="aMethodExpressionServiceTask"
flowable:expression="#{myService.doSomething()}"
flowable:resultVariable="myVar" />
在上例中,服务执行的结果(调用*'doSomething()‘方法的返回值),在服务执行完成后,会设置为名为’myVar’*的流程变量。
1.2.6 异常处理
当执行自定义逻辑时,通常需要捕获并在流程中处理特定的业务异常。Flowable提供了多种方式。
1.2.6.1 抛出BPMN错误
可以在服务任务或脚本任务的用户代码中抛出BPMN错误。可以在Java委托、脚本、表达式与委托表达式中,抛出特殊的FlowableException:BpmnError。引擎会捕获这个异常,并将其转发至合适的错误处理器,如错误边界事件或错误事件子流程。
public class ThrowBpmnErrorDelegate implements JavaDelegate {
public void execute(DelegateExecution execution) throws Exception {
try {
executeBusinessLogic();
} catch (BusinessException e) {
throw new BpmnError("BusinessExceptionOccurred");
}
}
}
构造函数的参数是错误代码。错误代码决定了处理这个错误的错误处理器。
这个机制只应该用于业务错误,需要通过流程中定义的错误边界事件或错误事件子流程处理。技术错误应该通过其他异常类型表现,并且通常不在流程内部处理。
1.2.6.2 异常映射
可以使用mapException
扩展,直接将Java异常映射至业务异常(错误)。单映射是最简单的形式:
<serviceTask id="servicetask1" flowable:class="...">
<extensionElements>
<flowable:mapException
errorCode="myErrorCode1">com.example.SomeException</flowable:mapException>
</extensionElements>
</serviceTask>
在上面的代码中,如果服务任务抛出org.flowable.SomeException
的实例,引擎会捕获该异常,并将其转换为带有给定errorCode的BPMN错误。然后就可以像普通BPMN错误完全一样地处理。其他的异常没有映射,仍将抛出至API调用处。
也可以在单行中使用includeChildExceptions
属性,映射特定异常的所有子异常。
<serviceTask id="servicetask1" flowable:class="...">
<extensionElements>
<flowable:mapException errorCode="myErrorCode1"
includeChildExceptions="true">com.example.SomeException</flowable:mapException>
</