这是我参与更文挑战的第4天,活动详情查看: 更文挑战
任务
所有图例
用户任务
-
描述
一个用户的任务是用来模型的工作,需要由人类演员来完成。当流程执行到达这样的用户任务时,会在分配给该任务的用户或组的任务列表中创建一个新任务。
-
图例
用户任务可视化为典型任务(圆角矩形),左上角有一个小用户图标。
-
在xml中表达式
用户任务在 XML 中定义如下。该ID是必需的属性,在名称属性是可选的。
<userTask id="theTask" name="Important task" />
用户任务也可以有描述。事实上,任何 BPMN 2.0 元素都可以有描述。描述是通过添加文档元素来定义的。
<userTask id="theTask" name="Schedule meeting" >
<documentation>
Schedule an engineering meeting for next week with the new hire.
</documentation>
</userTask>
用户任务扩展
到期日
每个任务都有一个字段,指示该任务的截止日期。Query API 可用于查询在特定日期之前、之前或之后到期的任务。
有一个活动扩展,它允许您在任务定义中指定一个表达式,以在创建任务时设置它的初始截止日期。该表达式应始终解析为java.util.Date
, java.util.String (ISO8601 formatted)
, ISO8601 持续时间(例如 PT50M)或null
。例如,您可以使用在流程中的先前表单中输入或在先前服务任务中计算的日期。如果使用 time-duration,到期日期将根据当前时间计算,增加给定的时间段。例如,当“PT30M”用作dueDate 时,任务将在30 分钟后到期。
<userTask id="theTask" name="Important task" activiti:dueDate="${dateVariable}"/>
任务的截止日期也可以使用TaskService
或 在TaskListener
使用传递的DelegateTask
.
用户分配
用户任务可以直接分配给用户。这是通过定义一个humanPerformer子元素来完成的。这样的humanPerformer定义需要一个实际定义用户的resourceAssignmentExpression。目前,仅支持正式表达式。
<userTask id='theTask' name='important task' >
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>kermit</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
只能将一名用户分配为任务的人工执行者。在 Activiti 术语中,此用户称为受让人。具有受托人的任务在其他人的任务列表中不可见,而是可以在受托人的所谓个人任务列表中找到。
任务也可以放在所谓的人员候选任务列表中。在这种情况下,必须使用potentialOwner构造。用法类似于humanPerformer构造。请注意,需要为形式表达式中的每个元素定义,以指定它是用户还是组(引擎无法猜测)。
<userTask id='theTask' name='important task' >
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>user(kermit), group(management)</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
用于任务分配的 Activiti 扩展
-
描述
很明显,对于分配不复杂的用例,用户和组分配非常麻烦。为了避免这些复杂性,可以对用户任务进行自定义扩展。
- 受让人属性:此自定义扩展允许直接将用户任务分配给给定用户。
<userTask id="theTask" name="my task" activiti:assignee="kermit" />
这与使用上述定义的humanPerformer构造完全相同。
- CandidateUsers 属性:此自定义扩展允许使用户成为任务的候选人。
1<userTask id="theTask" name="my task" activiti:candidateUsers="kermit, gonzo" />
这与使用上述定义的potentialOwner构造完全相同。请注意,不需要像潜在所有者构造那样使用*user(kermit)*声明,因为该属性只能用于用户。
- CandidateGroups 属性:这个自定义扩展允许让一个组成为任务的候选者。
1<userTask id="theTask" name="my task" activiti:candidateGroups="management, accountancy" />
这与使用上述定义的potentialOwner构造完全相同。请注意,不需要像潜在所有者构造那样使用*组(管理)*声明,因为该属性只能用于组。
- CandidateUsers和CandidateGroups都可以定义在同一个用户任务上。
注意:虽然 Activiti 提供了一个身份管理组件,该组件通过IdentityService公开,但不检查提供的用户是否被身份组件知道。这允许 Activiti 在嵌入到应用程序中时与现有的身份管理解决方案集成。
通过任务侦听器自定义分配
如果前面的方法还不够,可以使用create 事件上的任务侦听器委托自定义分配逻辑:
<userTask id="task1" name="My task" >
<extensionElements>
<activiti:taskListener event="create" class="org.activiti.MyAssignmentHandler" />
</extensionElements>
</userTask>
脚本任务
-
描述
脚本任务是一项自动活动。当流程执行到达脚本任务时,会执行相应的脚本。
-
图例
脚本任务可视化为典型的 BPMN 2.0 任务(圆角矩形),矩形左上角有一个小脚本图标。
-
在xml中表达式
脚本任务是通过指定脚本和scriptFormat来定义的。
<scriptTask id="theScriptTask" name="Execute script" scriptFormat="groovy"> <script> sum = 0 for ( i in inputArray ) { sum += i } </script> </scriptTask>
Java 服务任务
-
描述
Java 服务任务用于调用外部 Java 类。
-
图例
服务任务显示为左上角带有小齿轮图标的圆角矩形。
- 在xml中表达式
有 4 种方式声明如何调用 Java 逻辑:
- 指定实现 JavaDelegate 或 ActivityBehavior 的类
- 评估解析为委托对象的表达式
- 调用方法表达式
- 评估值表达式
要指定在流程执行期间调用的类,需要由*activiti:class*属性提供完全限定的类名。
<serviceTask id="javaService"
name="My Java Service Task"
activiti:class="org.activiti.MyJavaDelegate" />
有关如何使用此类的更多详细信息,请参阅实现部分。
也可以使用解析为对象的表达式。此对象必须遵循与activiti:class
使用该属性时创建的对象相同的规则(请参阅更多内容)。
<serviceTask id="serviceTask" activiti:delegateExpression="${delegateExpressionBean}" />
这里,delegateExpressionBean
是一个实现JavaDelegate
接口的 bean,例如在 Spring 容器中定义。
要指定应评估的 UEL 方法表达式,请使用属性activiti:expression。
<serviceTask id="javaService"
name="My Java Service Task"
activiti:expression="#{printer.printMessage()}" />
printMessage
将在名为 的命名对象上调用方法(不带参数)printer
。
也可以使用表达式中使用的方法传递参数。
<serviceTask id="javaService"
name="My Java Service Task"
activiti:expression="#{printer.printMessage(execution, myVar)}" />
printMessage
将在名为 的对象上调用方法printer
。传递的第一个参数是DelegateExecution
,默认情况下它在表达式上下文中可用作为execution
。传递的第二个参数是myVar
当前执行中带有 name 的变量的值。
要指定应该计算的 UEL 值表达式,请使用属性activiti:expression。
<serviceTask id="javaService"
name="My Java Service Task"
activiti:expression="#{split.ready}" />
财产的getter方法ready
,getReady
(不带参数),将在叫的bean被调用split
。命名对象在执行的流程变量和(如果适用)Spring 上下文中解析。
网络服务任务
-
描述
Web Service 任务用于同步调用外部 Web 服务。
-
图形符号
Web 服务任务的可视化与 Java 服务任务相同。
- 在xml中表达式
要使用 Web 服务,我们需要导入它的操作和复杂类型。这可以通过使用指向 Web 服务的 WSDL 的导入标记自动完成:
<import importType="http://schemas.xmlsoap.org/wsdl/"
location="http://localhost:63081/counter?wsdl"
namespace="http://webservice.activiti.org/" />
前面的声明告诉 Activiti 导入定义,但它不会为您创建项目定义和消息。假设我们要调用一个名为prettyPrint的特定方法,因此我们需要为请求和响应消息创建相应的消息和项目定义:
<message id="prettyPrintCountRequestMessage" itemRef="tns:prettyPrintCountRequestItem" />
<message id="prettyPrintCountResponseMessage" itemRef="tns:prettyPrintCountResponseItem" />
<itemDefinition id="prettyPrintCountRequestItem" structureRef="counter:prettyPrintCount" />
<itemDefinition id="prettyPrintCountResponseItem" structureRef="counter:prettyPrintCountResponse" />
在声明服务任务之前,我们必须定义实际引用 Web 服务的 BPMN 接口和操作。基本上,我们定义接口和所需操作的. 对于每个操作,我们重用之前定义的消息进行输入和输出。例如,以下声明定义了计数器接口和prettyPrintCountOperation操作:
<interface name="Counter Interface" implementationRef="counter:Counter">
<operation id="prettyPrintCountOperation" name="prettyPrintCount Operation"
implementationRef="counter:prettyPrintCount">
<inMessageRef>tns:prettyPrintCountRequestMessage</inMessageRef>
<outMessageRef>tns:prettyPrintCountResponseMessage</outMessageRef>
</operation>
</interface>
然后我们可以通过使用##WebService 实现和对Web 服务操作的引用来声明一个Web 服务任务。
<serviceTask id="webService"
name="Web service invocation"
implementation="##WebService"
operationRef="tns:prettyPrintCountOperation">
Web 服务任务 IO 规范
除非我们对数据输入和输出关联使用简单的方法(见下文),否则每个 Web 服务任务都需要声明一个 IO 规范,该规范说明哪些是任务的输入和输出。该方法非常简单且符合 BPMN 2.0,对于我们的 prettyPrint 示例,我们根据之前声明的项目定义定义输入和输出集:
<ioSpecification>
<dataInput itemSubjectRef="tns:prettyPrintCountRequestItem" id="dataInputOfServiceTask" />
<dataOutput itemSubjectRef="tns:prettyPrintCountResponseItem" id="dataOutputOfServiceTask" />
<inputSet>
<dataInputRefs>dataInputOfServiceTask</dataInputRefs>
</inputSet>
<outputSet>
<dataOutputRefs>dataOutputOfServiceTask</dataOutputRefs>
</outputSet>
</ioSpecification>
Web 服务任务数据输入关联
有两种指定数据输入关联的方法:
- 使用表达式
- 使用简单的方法
要使用表达式指定数据输入关联,我们需要定义源项和目标项并指定每个项的字段之间的相应分配。在以下示例中,我们为项目分配前缀和后缀字段:
<dataInputAssociation>
<sourceRef>dataInputOfProcess</sourceRef>
<targetRef>dataInputOfServiceTask</targetRef>
<assignment>
<from>${dataInputOfProcess.prefix}</from>
<to>${dataInputOfServiceTask.prefix}</to>
</assignment>
<assignment>
<from>${dataInputOfProcess.suffix}</from>
<to>${dataInputOfServiceTask.suffix}</to>
</assignment>
</dataInputAssociation>
另一方面,我们可以使用简单得多的方法。所述sourceRef元件是Activiti的变量名和targetRef元素是项定义的属性。在下面的例子中,我们分配给前缀字段中的变量的值PrefixVariable和所述后缀的变量的值字段SuffixVariable。
<dataInputAssociation>
<sourceRef>PrefixVariable</sourceRef>
<targetRef>prefix</targetRef>
</dataInputAssociation>
<dataInputAssociation>
<sourceRef>SuffixVariable</sourceRef>
<targetRef>suffix</targetRef>
</dataInputAssociation>
Web 服务任务数据输出关联
有两种指定数据输出关联的方法:
- 使用表达式
- 使用简单的方法
要使用表达式指定数据输出关联,我们需要定义目标变量和源表达式。该方法非常简单且类似的数据输入关联:
<dataOutputAssociation>
<targetRef>dataOutputOfProcess</targetRef>
<transformation>${dataOutputOfServiceTask.prettyPrint}</transformation>
</dataOutputAssociation>
另一方面,我们可以使用简单得多的方法。所述sourceRef元素是项定义的属性和targetRef元件是Activiti的变量名。该方法非常简单且类似的数据输入关联:
<dataOutputAssociation>
<sourceRef>prettyPrint</sourceRef>
<targetRef>OutputVariable</targetRef>
</dataOutputAssociation>
业务规则任务
-
描述
业务规则任务用于同步执行一个或多个规则。Activiti 使用 Drools Expert,即 Drools 规则引擎来执行业务规则。目前,包含业务规则的 .drl 文件必须与定义业务规则任务的流程定义一起部署以执行这些规则。这意味着流程中使用的所有 .drl 文件都必须打包在流程 BAR 文件中。
-
图例
业务规则任务通过表格图标进行可视化。
-
在xml中表达式
要执行部署在与流程定义相同的 BAR 文件中的一个或多个业务规则,我们需要定义输入和结果变量。对于输入变量定义,可以定义一个过程变量列表,以逗号分隔。输出变量定义只能包含一个变量名,用于将执行的业务规则的输出对象存储在流程变量中。请注意,结果变量将包含一个对象列表。如果默认情况下未指定结果变量名称,则使用 org.activiti.engine.rules.OUTPUT。
以下业务规则任务执行与流程定义一起部署的所有业务规则:
<process id="simpleBusinessRuleProcess">
<startEvent id="theStart" />
<sequenceFlow sourceRef="theStart" targetRef="businessRuleTask" />
<businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}"
activiti:resultVariable="rulesOutput" />
<sequenceFlow sourceRef="businessRuleTask" targetRef="theEnd" />
<endEvent id="theEnd" />
</process>
还可以将业务规则任务配置为仅执行已部署的 .drl 文件中定义的一组规则。为此必须指定由逗号分隔的规则名称列表。
<businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}"
activiti:rules="rule1, rule2" />
在这种情况下,只执行 rule1 和 rule2。
您还可以定义应从执行中排除的规则列表。
<businessRuleTask id="businessRuleTask" activiti:ruleVariablesInput="${order}"
activiti:rules="rule1, rule2" exclude="true" />
在这种情况下,除了 rule1 和 rule2 之外,所有部署在与流程定义相同的 BAR 文件中的规则都将被执行。
如前所述,另一种选择是自己挂钩 BusinessRuleTask 的实现:
<businessRuleTask id="businessRuleTask" activiti:class="${MyRuleServiceDelegate}" />
现在,BusinessRuleTask 的行为与 ServiceTask 完全一样,但仍保留 BusinessRuleTask 图标,以可视化我们在此处进行业务规则处理。
手动任务
-
描述
一个手动任务定义一个任务是外部的BPM引擎。它用于对某人完成的工作进行建模,引擎不需要知道这些工作,也不需要系统或 UI 界面。对于引擎,手动任务作为传递活动处理,从流程执行到达的那一刻起自动继续流程。
-
图形符号
手动任务被可视化为一个圆角矩形,左上角有一个小手形图标
- 在xml中表达式
<manualTask id="myManualTask" name="Call client for more information" />
接收任务
- 描述
接收任务是一个简单的任务,它等待某个消息的到来。目前,我们仅为此任务实现了 Java 语义。当流程执行到达接收任务时,流程状态被提交到持久性存储。这意味着进程将保持在此等待状态,直到引擎接收到特定消息,这会触发进程继续执行接收任务。
- 图形符号
接收任务可视化为左上角带有消息图标的任务(圆角矩形)。消息是白色的(黑色消息图标将具有发送语义)
- 在xml中表达式
<receiveTask id="waitState" name="wait" />
多实例任务
描述
一个多实例活动是定义用于业务流程的某些步骤的重复的方法。在编程概念中,多实例匹配for each构造:它允许对给定集合中的每个项目依次或并行执行某个步骤甚至完整的子流程。
阿多实例是具有定义(所谓的额外性质的常规活动*“多实例特性”'),这将导致在运行时将要执行的活动多次。以下活动可以成为多实例活动:*
- 用户任务
- 脚本任务
- Java 服务任务
- 网络服务任务
- 业务规则任务
- 电子邮件任务
- 手动任务
- 接收任务
- (嵌入式)子流程
- 通话活动
一个网关或事件不能成为多实例。
根据规范的要求,为每个实例创建的执行的每个父执行将具有以下变量:
- nrOfInstances : 实例总数
- nrOfActiveInstances:当前活动(即尚未完成)实例的数量。对于顺序多实例,这将始终为 1。
- nrOfCompletedInstances:已经完成的实例数。
可以通过调用该execution.getVariable(x)
方法来检索这些值。
此外,每个创建的执行都将有一个执行局部变量(即对于其他执行不可见,并且不存储在流程实例级别):
- loopCounter:指示该特定实例的 for-each 循环中的索引。loopCounter 变量可以通过 Activiti elementIndexVariable属性重命名。
图例
如果活动是多实例,则在该活动底部用三条短线表示。三条垂直线表示实例将并行执行,而三条水平线表示顺序执行。
在XML中 表达式
要使活动多实例,活动 xml 元素必须有一个multiInstanceLoopCharacteristics
子元素。
<multiInstanceLoopCharacteristics isSequential="false|true">
...
</multiInstanceLoopCharacteristics>
所述isSequential属性表示该活动的实例被顺序地或并行地执行。
实例数在进入活动时计算一次。有几种配置方法。方法是直接指定一个数字,通过使用loopCardinality子元素。
<multiInstanceLoopCharacteristics isSequential="false|true">
<loopCardinality>5</loopCardinality>
</multiInstanceLoopCharacteristics>
解析为正数的表达式也是可能的:
<multiInstanceLoopCharacteristics isSequential="false|true">
<loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality>
</multiInstanceLoopCharacteristics>
定义实例数量的另一种方法是使用loopDataInputRef
子元素指定作为集合的流程变量的名称。对于集合中的每个项目,将创建一个实例。或者,可以使用inputDataItem
子元素为实例设置集合的特定项目。这在以下 XML 示例中显示:
<userTask id="miTasks" name="My Task ${loopCounter}" activiti:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false">
<loopDataInputRef>assigneeList</loopDataInputRef>
<inputDataItem name="assignee" />
</multiInstanceLoopCharacteristics>
</userTask>
假设变量assigneeList
包含值\[kermit, gonzo, fozzie\]
。在上面的代码片段中,将并行创建三个用户任务。每个执行都将有一个名为的流程变量assignee
,其中包含集合的一个值,在此示例中用于分配用户任务。
loopDataInputRef
and的缺点inputDataItem
是 1) 名称很难记住 2) 由于 BPMN 2.0 模式限制,它们不能包含表达式。Activiti 通过在 上提供collection和elementVariable属性来解决这个问题multiInstanceCharacteristics
:
<userTask id="miTasks" name="My Task" activiti:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="true"
activiti:collection="${myService.resolveUsersForTask()}" activiti:elementVariable="assignee" >
</multiInstanceLoopCharacteristics>
</userTask>
当所有实例都完成时,多实例活动结束。但是,可以指定每次实例结束时计算的表达式。当这个表达式的计算结果为真时,所有剩余的实例都会被销毁,多实例活动结束,继续这个过程。这样的表达式必须在completionCondition子元素中定义。
<userTask id="miTasks" name="My Task" activiti:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false"
activiti:collection="assigneeList" activiti:elementVariable="assignee" >
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
在此示例中,将为assigneeList
集合的每个元素创建并行实例。但是,当完成 60% 的任务时,将删除其他任务并继续该过程。
边界事件和多实例
由于多实例是常规活动,因此可以在其边界上定义边界事件。在中断边界事件的情况下,当事件被捕获时,所有仍处于活动状态的实例将被销毁。以下面的多实例子流程为例:
在这里,当计时器触发时,子流程的所有实例都将被销毁,无论有多少实例或当前尚未完成哪些内部活动。
多实例和执行侦听器
(适用于 Activiti 5.18 及更高版本)
将执行侦听器与多实例结合使用时有一个警告。以下面的 BPMN 2.0 xml 片段为例,它定义在与设置multiInstanceLoopCharacteristics xml 元素相同的级别上:
<extensionElements>
<activiti:executionListener event="start" class="org.activiti.MyStartListener"/>
<activiti:executionListener event="end" class="org.activiti.MyEndListener"/>
</extensionElements>
对于正常的 BPMN 活动,在活动开始和结束时会调用这些侦听器。
但是,当活动是多实例时,行为是不同的:
- 当进入多实例活动时,在执行任何内部活动之前,会抛出一个开始事件。该循环计数器变量尚未设置(为空)。
- 对于访问的每个实际活动,都会抛出一个开始事件。该循环计数器变量。
同样的推理适用于结束事件:
- 当实际活动结束时,会抛出一个结束偶数。该循环计数器变量。
- 当多实例活动作为一个整体完成时,会抛出一个结束事件。该循环计数器变量没有设置。
例如:
<subProcess id="subprocess1" name="Sub Process">
<extensionElements>
<activiti:executionListener event="start" class="org.activiti.MyStartListener"/>
<activiti:executionListener event="end" class="org.activiti.MyEndListener"/>
</extensionElements>
<multiInstanceLoopCharacteristics isSequential="false">
<loopDataInputRef>assignees</loopDataInputRef>
<inputDataItem name="assignee"></inputDataItem>
</multiInstanceLoopCharacteristics>
<startEvent id="startevent2" name="Start"></startEvent>
<endEvent id="endevent2" name="End"></endEvent>
<sequenceFlow id="flow3" name="" sourceRef="startevent2" targetRef="endevent2"></sequenceFlow>
</subProcess>
在此示例中,假设受理人列表包含三个项目。运行时会发生以下情况:
- 为整个多实例抛出一个开始事件。在开始执行监听器被调用。该循环计数器也不受让人变量将不会被设置(即,它们将是无效的)。
- 为每个活动实例抛出一个开始事件。在开始执行监听器被调用三次。该循环计数器也不受让人变量将被设置(即,从空不同)。
- 因此,启动执行侦听器总共被调用了四次。
请注意,当multiInstanceLoopCharacteristics也定义在子进程之外的其他东西上时,这同样适用。例如,如果上面的例子是一个简单的 userTask,同样的推理仍然适用。
子流程和调用活动
子流程
描述
甲子过程是包含其他活动,网关,事件等,其在其自身上形成的过程,是更大的处理的一部分的活性。甲子过程是一个父进程内完全确定(这就是为什么它通常被称为一个嵌入子过程)。
子流程有两个主要用例:
- 子流程允许分层建模。许多建模工具允许折叠子流程,隐藏子流程的所有细节并显示业务流程的高级端到端概述。
- 子流程为 events创建了一个新范围。在子流程执行期间抛出的事件可以被子流程边界上的边界事件捕获,从而为该事件创建仅限于子流程的范围。
使用子流程确实会施加一些限制:
- 一个子流程只能有一个无开始事件,不允许有其他的开始事件类型。一个子流程必须至少有一个结束事件。请注意,BPMN 2.0 规范允许省略子流程中的开始和结束事件,但当前的 Activiti 实现不支持这一点。
- 序列流不能跨越子流程边界。
图例
子流程可视化为典型的活动,即圆角矩形。如果子流程被折叠,则只显示名称和加号,提供流程的高级概述:
如果子流程被展开,子流程的步骤将显示在子流程边界内:
使用子流程的主要原因之一是为某个事件定义范围。以下流程模型显示了这一点:调查软件/调查硬件任务都需要并行完成,但是这两项任务都需要在咨询级别 2 支持之前的特定时间内完成。在这里,计时器的范围(即哪些活动必须及时完成)受子流程的约束。
在XML中表达式
子流程由子流程元素定义。作为子流程一部分的所有活动、网关、事件等都需要包含在此元素中。
<subProcess id="subProcess">
<startEvent id="subProcessStart" />
... other Sub-Process elements ...
<endEvent id="subProcessEnd" />
</subProcess>