Flowable實(shí)戰(zhàn)(八)BPMN2.0 任務(wù)

??任務(wù)是流程中最重要的組成部分塑悼。Flowable提供了多種任務(wù)類(lèi)型,以滿(mǎn)足實(shí)際需求价脾。

??常用任務(wù)類(lèi)型有:

  • 用戶(hù)任務(wù)

  • Java Service任務(wù)

  • 腳本任務(wù)

  • 業(yè)務(wù)規(guī)則任務(wù)

  • 執(zhí)行監(jiān)聽(tīng)器

  • 任務(wù)監(jiān)聽(tīng)器

  • 多實(shí)例

??集成擴(kuò)展的任務(wù)類(lèi)型有:

  • 手動(dòng)任務(wù)

  • Java接收任務(wù)

  • Shell任務(wù)

  • 補(bǔ)償處理器

  • Web Service任務(wù)

  • 郵件任務(wù)

  • Http任務(wù)

  • Camel任務(wù)

  • Mule任務(wù)

??任務(wù)的圖形都是以一個(gè)圓角矩形為基礎(chǔ)推汽,在左上角添加具體類(lèi)型的圖標(biāo)。

一瘦馍、常用的任務(wù)類(lèi)型

1.1 用戶(hù)任務(wù)

1.1.1 描述

??“用戶(hù)任務(wù)(user task)”指需要人工執(zhí)行的任務(wù)。當(dāng)流程執(zhí)行到達(dá)用戶(hù)任務(wù)時(shí)应役,流程實(shí)例會(huì)停止等待情组,直到用戶(hù)觸發(fā)完成任務(wù)動(dòng)作。

1.1.2 圖示

??用戶(hù)任務(wù)用左上角有一個(gè)小用戶(hù)圖標(biāo)的標(biāo)準(zhǔn)任務(wù)(圓角矩形)表示箩祥。

用戶(hù)任務(wù).png

1.1.3 XML表示

??用戶(hù)任務(wù)在XML中如下定義院崇。其中id是必須屬性,name是可選屬性袍祖。

    <userTask id="theTask" name="重要任務(wù)" />

1.1.4 到期日期

??每個(gè)任務(wù)都可以設(shè)置到期日期(due date)底瓣。

??可以指定固定時(shí)間或相對(duì)時(shí)間,比如蕉陋,當(dāng)dueDate為“PT30M”時(shí)捐凭,表示到達(dá)任務(wù)30分鐘后到期。

??到期日期必須符合java.util.Date或java.util.String(ISO8601格式)凳鬓。

??實(shí)際應(yīng)用茁肠,我們指定為變量值。

    <userTask id="theTask" name="Important task" flowable:dueDate="${dateVariable}"/>

??任務(wù)的到期日期可以使用TaskService缩举,或者在TaskListener中使用傳遞的DelegateTask修改垦梆。

1.1.5 任務(wù)指派

  • 指派確定的辦理人
    <userTask id="theTask" name="重要任務(wù)" flowable:assignee="jinyangjie"/>
  • 指派潛在辦理人
    <userTask id="theTask" name="重要任務(wù)" flowable:candidateUsers="jinyangjie, zhangsan" />
  • 指派潛在辦理組
    <userTask id="theTask" name="重要任務(wù)" flowable:candidateGroups="leader, manager" />

??更多任務(wù)指派的內(nèi)容匹颤,已在“用戶(hù)和組”的篇章中介紹,這里不再贅述托猩。

1.2 Java Service任務(wù)

1.2.1 描述

??Java Service任務(wù)(Java service task)用于調(diào)用Java類(lèi)印蓖。Java Service不屬于BPMN2.0規(guī)范,而是Flowable的自定義擴(kuò)展京腥。

1.2.2 圖示

??服務(wù)任務(wù)用左上角有一個(gè)小齒輪圖標(biāo)的圓角矩形表示另伍。

javaService.png

1.2.3 XML表示

??有三種方法聲明如何調(diào)用Java邏輯,下面分別介紹:

  • 調(diào)用固定的類(lèi)

??使用flowable:class屬性提供全限定類(lèi)名(fully qualified classname)绞旅,指定流程執(zhí)行時(shí)調(diào)用的類(lèi),該類(lèi)必須實(shí)現(xiàn)JavaDelegate或ActivityBehavior接口温艇。

    <serviceTask id="javaService" flowable:class="com.example.service.MyJavaDelegate" />
  • 調(diào)用動(dòng)態(tài)類(lèi)

??使用flowable:delegateExpression屬性提供委托對(duì)象(delegation object)的表達(dá)式因悲。該功能和flowable:class類(lèi)似,同樣需要實(shí)現(xiàn)JavaDelegate或ActivityBehavior接口勺爱,只不過(guò)這里不是指定一個(gè)具體的實(shí)現(xiàn)類(lèi)晃琳,而是查詢(xún)指定名稱(chēng)的Bean對(duì)象。

    <serviceTask id="javaService" flowable:delegateExpression="${myDelegateExpressionBean}" />

??myDelegateExpressionBean是一個(gè)實(shí)現(xiàn)了JavaDelegate接口的bean琐鲁,定義在Spring容器中卫旱。

  • 調(diào)用類(lèi)的指定方法或?qū)傩灾?/strong>

??使用flowable:expression屬性指定類(lèi)的方法或?qū)傩灾怠M瑯拥奈Ф危擃?lèi)需要實(shí)現(xiàn)JavaDelegate或ActivityBehavior接口顾翼。

    <serviceTask id="javaService" flowable:expression="#{printer.printMessage()}" />

??將在名為printer的對(duì)象上調(diào)用printMessage方法(不帶參數(shù))。當(dāng)然也可以為表達(dá)式中使用的方法傳遞變量奈泪。

??屬性值示例:

    <serviceTask id="javaService" flowable:expression="#{printer.ready}" />

??會(huì)調(diào)用名為printer的bean的ready參數(shù)的getter方法适贸,getReady(不帶參數(shù))。該值會(huì)被解析為執(zhí)行的流程變量涝桅。

1.2.4 具體實(shí)現(xiàn)實(shí)例

??下面是一個(gè)Java類(lèi)的示例拜姿,用于將流程變量String改為大寫(xiě)。這個(gè)類(lèi)通過(guò)實(shí)現(xiàn)org.flowable.engine.delegate.JavaDelegate接口冯遂,可以在流程執(zhí)行中被調(diào)用蕊肥。

??同時(shí),需要重寫(xiě)execute(DelegateExecution)方法實(shí)現(xiàn)業(yè)務(wù)邏輯蛤肌。這個(gè)方法就是引擎將調(diào)用的方法壁却。另外,通過(guò)該方法中的DelegateExecution參數(shù)可以訪問(wèn)流程實(shí)例的各種信息寻定。

    public class ToUppercase implements JavaDelegate {
      public void execute(DelegateExecution execution) {
        String var = (String) execution.getVariable("input");
        var = var.toUpperCase();
        execution.setVariable("input", var);
      }
    }

??如果實(shí)現(xiàn)org.flowable.engine.impl.delegate.ActivityBehavior接口儒洛,可以訪問(wèn)更強(qiáng)大的引擎功能,例如狼速,可以影響流程的控制流程琅锻。但注意這并不是好的實(shí)踐,需要避免這么使用。

1.2.5 任務(wù)的返回值

??服務(wù)執(zhí)行的返回值(僅對(duì)使用表達(dá)式的服務(wù)任務(wù))恼蓬,可以通過(guò)為服務(wù)任務(wù)定義的'flowable:resultVariable'屬性設(shè)置為流程變量惊完。可以是已經(jīng)存在的处硬,或者新的流程變量小槐。 如果指定為已存在的流程變量,則流程變量的值會(huì)被服務(wù)執(zhí)行的返回值覆蓋荷辕。 如果不指定結(jié)果變量名凿跳,則服務(wù)任務(wù)的返回值將被忽略。

    <serviceTask id="aMethodExpressionServiceTask"
        flowable:expression="#{myService.doSomething()}"
        flowable:resultVariable="myVar" />

??在上例中疮方,服務(wù)執(zhí)行的結(jié)果(調(diào)用'doSomething()'方法的返回值)控嗜,在服務(wù)執(zhí)行完成后,會(huì)設(shè)置為名為'myVar'的流程變量骡显。

1.2.6 異常處理

??當(dāng)執(zhí)行自定義邏輯時(shí)疆栏,通常需要捕獲并在流程中處理特定的業(yè)務(wù)異常。Flowable提供了多種方式惫谤。

1.2.6.1 拋出BPMN錯(cuò)誤

??可以在服務(wù)任務(wù)或腳本任務(wù)的用戶(hù)代碼中拋出BPMN錯(cuò)誤壁顶。可以在Java委托溜歪、腳本若专、表達(dá)式與委托表達(dá)式中,拋出特殊的FlowableException:BpmnError痹愚。引擎會(huì)捕獲這個(gè)異常富岳,并將其轉(zhuǎn)發(fā)至合適的錯(cuò)誤處理器,如錯(cuò)誤邊界事件或錯(cuò)誤事件子流程拯腮。

    public class ThrowBpmnErrorDelegate implements JavaDelegate {
      public void execute(DelegateExecution execution) throws Exception {
        try {
          executeBusinessLogic();
        } catch (BusinessException e) {
          throw new BpmnError("BusinessExceptionOccurred");
        }
      }
    }

??構(gòu)造函數(shù)的參數(shù)是錯(cuò)誤代碼窖式。錯(cuò)誤代碼決定了處理這個(gè)錯(cuò)誤的錯(cuò)誤處理器。

??這個(gè)機(jī)制只應(yīng)該用于業(yè)務(wù)錯(cuò)誤动壤,需要通過(guò)流程中定義的錯(cuò)誤邊界事件或錯(cuò)誤事件子流程處理萝喘。技術(shù)錯(cuò)誤應(yīng)該通過(guò)其他異常類(lèi)型表現(xiàn),并且通常不在流程內(nèi)部處理琼懊。

1.2.6.2 異常映射

??可以使用mapException擴(kuò)展阁簸,直接將Java異常映射至業(yè)務(wù)異常(錯(cuò)誤)。單映射是最簡(jiǎn)單的形式:

    <serviceTask id="servicetask1" flowable:class="...">
      <extensionElements>
        <flowable:mapException
              errorCode="myErrorCode1">com.example.SomeException</flowable:mapException>
      </extensionElements>
    </serviceTask>

??在上面的代碼中哼丈,如果服務(wù)任務(wù)拋出org.flowable.SomeException的實(shí)例启妹,引擎會(huì)捕獲該異常,并將其轉(zhuǎn)換為帶有給定errorCode的BPMN錯(cuò)誤醉旦。然后就可以像普通BPMN錯(cuò)誤完全一樣地處理拄轻。其他的異常沒(méi)有映射,仍將拋出至API調(diào)用處不瓶。

??也可以在單行中使用includeChildExceptions屬性,映射特定異常的所有子異常照瘾。

    <serviceTask id="servicetask1" flowable:class="...">
      <extensionElements>
        <flowable:mapException errorCode="myErrorCode1"
               includeChildExceptions="true">com.example.SomeException</flowable:mapException>
      </extensionElements>
    </serviceTask>

??上面的代碼中,F(xiàn)lowable會(huì)將SomeException的任何直接或間接的子類(lèi)丧慈,轉(zhuǎn)換為帶有指定錯(cuò)誤代碼的BPMN錯(cuò)誤析命。 當(dāng)未指定includeChildExceptions時(shí),視為“false”逃默。

1.2.6.3 默認(rèn)映射

??默認(rèn)映射最常用鹃愤。默認(rèn)映射是一個(gè)不指定類(lèi)的映射,可以匹配任何Java異常:

    <serviceTask id="servicetask1" flowable:class="...">
      <extensionElements>
        <flowable:mapException errorCode="myErrorCode1"/>
      </extensionElements>
    </serviceTask>

??除了默認(rèn)映射完域,會(huì)按照從上至下的順序檢查映射昼浦,使用第一個(gè)匹配的映射。只在所有映射都不能成功匹配時(shí)使用默認(rèn)映射筒主。 只有第一個(gè)不指定類(lèi)的映射會(huì)作為默認(rèn)映射。默認(rèn)映射忽略includeChildExceptions鸟蟹。

1.2.6.4 異常順序流

??還有種推薦用法乌妙,在發(fā)生異常時(shí),將流程執(zhí)行路由至另一條路徑建钥。下面是一個(gè)例子藤韵。

    <serviceTask id="servicetask1" flowable:class="com.example.ThrowsExceptionBehavior">
    </serviceTask>

    <sequenceFlow id="no-exception" sourceRef="javaService" targetRef="theEnd" />
    <sequenceFlow id="exception" sourceRef="javaService" targetRef="fixException" />

??服務(wù)任務(wù)有兩條出口順序流,命名為exceptionno-exception熊经。在發(fā)生異常時(shí)泽艘,使用順序流ID控制流程流向:

    public class ThrowsExceptionBehavior implements ActivityBehavior {

      public void execute(DelegateExecution execution) {
        String var = (String) execution.getVariable("var");

        String sequenceFlowToTake = null;
        try {
          executeLogic(var);
          sequenceFlowToTake = "no-exception";
        } catch (Exception e) {
          sequenceFlowToTake = "exception";
        }
        DelegateHelper.leaveDelegate(execution, sequenceFlowToTake);
      }

    }

1.3 腳本任務(wù)

1.3.1 描述

??腳本任務(wù)(script task)是自動(dòng)執(zhí)行的活動(dòng)。當(dāng)流程執(zhí)行到達(dá)腳本任務(wù)時(shí)镐依,會(huì)執(zhí)行相應(yīng)的腳本匹涮。

1.3.2 圖示

??腳本任務(wù)用左上角有一個(gè)小“腳本”圖標(biāo)的標(biāo)準(zhǔn)BPMN 2.0任務(wù)(圓角矩形)表示。

腳本任務(wù).png

1.3.3 XML表示

??腳本任務(wù)使用scriptscriptFormat元素定義槐壳。

    <scriptTask id="theScriptTask" scriptFormat="groovy">
      <script>
        sum = 0
        for ( i in inputArray ) {
          sum += i
        }
      </script>
    </scriptTask>

??默認(rèn)情況下然低,JavaScript包含在每一個(gè)JDK中,因此不需要添加任何JAR文件务唐。如果想使用其它腳本引擎雳攘,則需要在classpath中添加相應(yīng)的jar,并使用適當(dāng)?shù)拿址愕选@缍置穑現(xiàn)lowable單元測(cè)試經(jīng)常使用Groovy。Groovy腳本引擎與groovy-all JAR捆綁在一起刑巧。添加如下依賴(lài):

    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-all</artifactId>
        <version>2.x.x<version>
    </dependency>

1.3.4 腳本中的變量

??到達(dá)腳本引擎的執(zhí)行中喧兄,所有的流程變量都可以在腳本中使用无畔。在這個(gè)例子里,腳本變量'inputArray'實(shí)際上就是一個(gè)流程變量(一個(gè)integer的數(shù)組)繁莹。

    <script>
        sum = 0
        for ( i in inputArray ) {
          sum += i
        }
    </script>

??在腳本中設(shè)置變量的例子:

    <script>
        def scriptVar = "test123"
        execution.setVariable("myVar", scriptVar)
    </script>

注意:下列名字是保留字檩互,不能用于變量名:out,out:print咨演,lang:import闸昨,context,elcontext薄风。

1.3.5 腳本任務(wù)的結(jié)果

??腳本任務(wù)的返回值饵较,可以通過(guò)為腳本任務(wù)定義的'flowable:resultVariable'屬性設(shè)置為流程變量≡饴福可以是已經(jīng)存在的循诉,或者新的流程變量。如果指定為已存在的流程變量撇他,則流程變量的值會(huì)被腳本執(zhí)行的結(jié)果值覆蓋茄猫。如果不指定結(jié)果變量名,則腳本結(jié)果值將被忽略困肩。

    <scriptTask id="theScriptTask" scriptFormat="juel" flowable:resultVariable="myVar">
      <script>#{echo}</script>
    </scriptTask>

??在上面的例子中划纽,腳本執(zhí)行的結(jié)果(解析表達(dá)式'#{echo}'的值),將在腳本完成后锌畸,設(shè)置為名為'myVar'的流程變量勇劣。

1.4 業(yè)務(wù)規(guī)則任務(wù)

1.4.1 描述

??在企業(yè)應(yīng)用中,推薦做法是使用可維護(hù)的規(guī)則庫(kù)來(lái)管理復(fù)雜多變的業(yè)務(wù)規(guī)則潭枣,將業(yè)務(wù)代碼和規(guī)則分開(kāi)維護(hù)比默,一旦規(guī)則有變動(dòng),只需修改預(yù)設(shè)規(guī)則即可盆犁,而不會(huì)影響到業(yè)務(wù)代碼命咐。

??業(yè)務(wù)規(guī)則任務(wù)可以根據(jù)流程變量的值處理預(yù)設(shè)的業(yè)務(wù)規(guī)則。Flowable支持目前最流行的規(guī)則引擎——Drools谐岁。只需把含有業(yè)務(wù)規(guī)則任務(wù)的流程文件和規(guī)則引擎文件“.drl”一同打包部署到系統(tǒng)中侈百,同時(shí)添加Drools的jar包,即可實(shí)現(xiàn)Flowable驅(qū)動(dòng)規(guī)則引擎翰铡。

1.4.2 圖示

??業(yè)務(wù)規(guī)則任務(wù)顯示為帶有表格圖標(biāo)的圓角矩形钝域。

業(yè)務(wù)規(guī)則任務(wù).png

1.4.3 XML表示

??要執(zhí)行業(yè)務(wù)規(guī)則,需要定義輸入與結(jié)果變量锭魔。輸入變量可以用流程變量的列表定義例证,使用逗號(hào)分隔。輸出變量只能有一個(gè)變量名迷捧,如果沒(méi)有指定結(jié)果變量名织咧,默認(rèn)為org.flowable.engine.rules.OUTPUT胀葱。

    <process id="simpleBusinessRuleProcess">
      <startEvent id="theStart" />
      <sequenceFlow sourceRef="theStart" targetRef="businessRuleTask" />

      <businessRuleTask id="businessRuleTask" flowable:ruleVariablesInput="${order}"
          flowable:resultVariable="rulesOutput" />

      <sequenceFlow sourceRef="businessRuleTask" targetRef="theEnd" />

      <endEvent id="theEnd" />
    </process>

??也可以將業(yè)務(wù)規(guī)則任務(wù)配置為只執(zhí)行部署的.drl文件中的一組規(guī)則。要做到這一點(diǎn)笙蒙,需要指定規(guī)則名字的列表抵屿,用逗號(hào)分隔。

    <businessRuleTask id="businessRuleTask" flowable:ruleVariablesInput="${order}"
          flowable:rules="rule1, rule2" />

??這樣只會(huì)執(zhí)行rule1與rule2捅位。

??也可以定義需要從執(zhí)行中排除的規(guī)則列表轧葛。

    <businessRuleTask id="businessRuleTask" flowable:ruleVariablesInput="${order}"
          flowable:rules="rule1, rule2" exclude="true" />

??這個(gè)例子中,除了rule1與rule2之外艇搀,其它所有與流程定義一起部署的規(guī)則都會(huì)被執(zhí)行尿扯。

注意:集成Drools的業(yè)務(wù)規(guī)則任務(wù),是企業(yè)應(yīng)用中的重要內(nèi)容焰雕,需要重點(diǎn)掌握衷笋。

1.5 執(zhí)行監(jiān)聽(tīng)器

1.5.1 描述

??執(zhí)行監(jiān)聽(tīng)器(execution listener)可以在流程執(zhí)行中發(fā)生特定的事件時(shí),執(zhí)行外部Java代碼或計(jì)算表達(dá)式矩屁”僮冢可以被捕獲的事件有:

  • 流程實(shí)例的啟動(dòng)和結(jié)束。
  • 流程執(zhí)行轉(zhuǎn)移吝秕。
  • 活動(dòng)的啟動(dòng)和結(jié)束慢蜓。
  • 網(wǎng)關(guān)的啟動(dòng)和結(jié)束。
  • 中間事件的啟動(dòng)和結(jié)束郭膛。
  • 啟動(dòng)事件的結(jié)束,和結(jié)束事件的啟動(dòng)氛悬。

1.5.2 XML表示

??下面的流程定義包含了三個(gè)執(zhí)行監(jiān)聽(tīng)器:

<process id="executionListenersProcess">

  <extensionElements>
    <flowable:executionListener
      class="org.flowable.examples.bpmn.executionlistener.ExampleExecutionListenerOne"
      event="start" />
  </extensionElements>

  <startEvent id="theStart" />
  <sequenceFlow sourceRef="theStart" targetRef="firstTask" />

  <userTask id="firstTask" />
  <sequenceFlow sourceRef="firstTask" targetRef="secondTask">
    <extensionElements>
      <flowable:executionListener
        class="org.flowable.examples.bpmn.executionListener.ExampleExecutionListenerTwo" />
    </extensionElements>
  </sequenceFlow>

  <userTask id="secondTask" >
    <extensionElements>
      <flowable:executionListener
        expression="${myPojo.myMethod(execution.event)}"
        event="end" />
    </extensionElements>
  </userTask>
  <sequenceFlow sourceRef="secondTask" targetRef="thirdTask" />

  <userTask id="thirdTask" />
  <sequenceFlow sourceRef="thirdTask" targetRef="theEnd" />

  <endEvent id="theEnd" />

</process>

??第一個(gè)執(zhí)行監(jiān)聽(tīng)器將在流程啟動(dòng)時(shí)收到通知则剃。這個(gè)監(jiān)聽(tīng)器是一個(gè)外部Java類(lèi)(ExampleExecutionListenerOne),并且需要實(shí)現(xiàn)org.flowable.engine.delegate.ExecutionListener接口如捅。當(dāng)該事件發(fā)生時(shí)(這里是start事件)棍现,會(huì)調(diào)用notify(ExecutionListenerExecution execution)方法。

    public class ExampleExecutionListenerOne implements ExecutionListener {

      public void notify(ExecutionListenerExecution execution) throws Exception {
        execution.setVariable("variableSetInExecutionListener", "firstValue");
        execution.setVariable("eventReceived", execution.getEventName());
      }
    }

??也可以使用實(shí)現(xiàn)了org.flowable.engine.delegate.JavaDelegate接口的委托類(lèi)镜遣。這些委托類(lèi)也可以用于其他的結(jié)構(gòu)己肮,如服務(wù)任務(wù)的委托。

??第二個(gè)執(zhí)行監(jiān)聽(tīng)器在流程執(zhí)行轉(zhuǎn)移時(shí)被調(diào)用悲关。請(qǐng)注意listener元素并未定義event谎僻,因?yàn)樵谵D(zhuǎn)移上只會(huì)觸發(fā)take事件。當(dāng)監(jiān)聽(tīng)器定義在轉(zhuǎn)移上時(shí)寓辱,event屬性的值將被忽略艘绍。

最后一個(gè)執(zhí)行監(jiān)聽(tīng)器在secondTask活動(dòng)結(jié)束時(shí)被調(diào)用。監(jiān)聽(tīng)器聲明中沒(méi)有使用class秫筏,而是定義了expression诱鞠。這個(gè)表達(dá)式將在事件觸發(fā)時(shí)計(jì)算/調(diào)用挎挖。

    <flowable:executionListener expression="${myPojo.myMethod(execution.eventName)}" event="end" />

??與其他表達(dá)式一樣,可以使用與解析execution變量航夺。

1.5.3 執(zhí)行監(jiān)聽(tīng)器上的字段注入

??使用通過(guò)class屬性配置的執(zhí)行監(jiān)聽(tīng)器時(shí)蕉朵,可以使用字段注入。

??下面的代碼片段展示了一個(gè)簡(jiǎn)單的示例流程阳掐,帶有一個(gè)使用了字段注入的執(zhí)行監(jiān)聽(tīng)器始衅。

<process id="executionListenersProcess">
  <extensionElements>
    <flowable:executionListener
        class="org.flowable.examples.bpmn.executionListener.ExampleFieldInjectedExecutionListener"
        event="start">

      <flowable:field name="fixedValue" stringValue="Yes, I am " />
      <flowable:field name="dynamicValue" expression="${myVar}" />

    </flowable:executionListener>
  </extensionElements>

  <startEvent id="theStart" />
  <sequenceFlow sourceRef="theStart" targetRef="firstTask" />

  <userTask id="firstTask" />
  <sequenceFlow sourceRef="firstTask" targetRef="theEnd" />

  <endEvent id="theEnd" />
</process>

??ExampleFieldInjectedExecutionListener類(lèi)將連接兩個(gè)字段(一個(gè)是固定值-fixedValue,另一個(gè)是動(dòng)態(tài)值-dynamicValue)锚烦,并將其存儲(chǔ)在'var'流程變量中觅闽。

    @Deployment(resources = {
      "org/flowable/examples/bpmn/executionListener/ExecutionListenersFieldInjectionProcess.bpmn20.xml"})
    public void testExecutionListenerFieldInjection() {
      Map<String, Object> variables = new HashMap<String, Object>();
      variables.put("myVar", "listening!");

      ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
          "executionListenersProcess", variables);

      Object varSetByListener = runtimeService.getVariable(processInstance.getId(), "var");
      assertNotNull(varSetByListener);
      assertTrue(varSetByListener instanceof String);

      // 結(jié)果為固定注入字段及注入表達(dá)式的連接
      assertEquals("Yes, I am listening!", varSetByListener);
    }

1.6 任務(wù)監(jiān)聽(tīng)器

1.6.1 描述

??任務(wù)監(jiān)聽(tīng)器(task listener)用于在特定的任務(wù)相關(guān)事件發(fā)生時(shí),執(zhí)行自定義的Java邏輯或表達(dá)式涮俄。

1.6.2 XML表示

??任務(wù)監(jiān)聽(tīng)器只能在流程定義中作為用戶(hù)任務(wù)的子元素蛉拙。請(qǐng)注意,任務(wù)監(jiān)聽(tīng)器是一個(gè)Flowable自定義結(jié)構(gòu)彻亲,因此也需要作為BPMN 2.0 extensionElements孕锄,放在flowable命名空間下。

    <userTask id="myTask" >
      <extensionElements>
        <flowable:taskListener event="create" class="com.example.MyTaskCreateListener" />
      </extensionElements>
    </userTask>

1.6.3 任務(wù)監(jiān)聽(tīng)器屬性:

1.6.3.1 event

??觸發(fā)任務(wù)監(jiān)聽(tīng)器的任務(wù)事件類(lèi)型苞尝,必填項(xiàng)畸肆。可用的事件有:

  • create(創(chuàng)建):當(dāng)任務(wù)已經(jīng)創(chuàng)建宙址,并且所有任務(wù)參數(shù)都已經(jīng)設(shè)置時(shí)觸發(fā)轴脐。
  • assignment(指派):當(dāng)任務(wù)已經(jīng)指派給某人時(shí)觸發(fā)。請(qǐng)注意:當(dāng)流程執(zhí)行到達(dá)用戶(hù)任務(wù)時(shí)抡砂,在觸發(fā)create事件之前大咱,會(huì)首先觸發(fā)assignment事件。這順序看起來(lái)不太自然注益,但是有實(shí)際原因的:當(dāng)收到create事件時(shí)碴巾,我們通常希望能看到任務(wù)的所有參數(shù),包括辦理人丑搔。
  • complete(完成):當(dāng)任務(wù)已經(jīng)完成厦瓢,從運(yùn)行時(shí)數(shù)據(jù)中刪除前觸發(fā)。
  • delete(刪除):在任務(wù)即將被刪除前觸發(fā)啤月。請(qǐng)注意任務(wù)由completeTask正常完成時(shí)也會(huì)觸發(fā)煮仇。
1.6.3.2 class

??需要調(diào)用的委托類(lèi)。這個(gè)類(lèi)必須實(shí)現(xiàn)org.flowable.engine.delegate.TaskListener接口谎仲。

    public class MyTaskCreateListener implements TaskListener {
      public void notify(DelegateTask delegateTask) {
        // 這里是要實(shí)現(xiàn)的業(yè)務(wù)邏輯
      }
    }

??也可以使用字段注入欺抗,為委托類(lèi)傳遞流程變量或執(zhí)行。請(qǐng)注意委托類(lèi)的實(shí)例在流程部署時(shí)創(chuàng)建(與Flowable中其它的委托類(lèi)一樣)强重,這意味著該實(shí)例會(huì)在所有流程實(shí)例執(zhí)行中共享绞呈。

1.6.3.3 expression

??指定在事件發(fā)生時(shí)要執(zhí)行的表達(dá)式(不能與class屬性一起使用)贸人。可以為被調(diào)用的對(duì)象傳遞DelegateTask對(duì)象與事件名(使用task.eventName)作為參數(shù)佃声。

    <flowable:taskListener event="create" expression="${myObject.callMethod(task, task.eventName)}" />
1.6.3.4 delegateExpression

??指定一個(gè)能夠解析為TaskListener接口實(shí)現(xiàn)類(lèi)的對(duì)象的表達(dá)式艺智。

    <flowable:taskListener event="create" delegateExpression="${myTaskListenerBean}" />

1.7 多實(shí)例

1.7.1 描述

??多實(shí)例活動(dòng)(multi-instance activity)是在業(yè)務(wù)流程中,為特定步驟定義重復(fù)的方式圾亏。在編程概念中十拣,多實(shí)例類(lèi)似for each結(jié)構(gòu):可以為給定集合中的每一條目,順序或并行地志鹃,執(zhí)行特定步驟夭问,甚至是整個(gè)子流程。

??網(wǎng)關(guān)和事件不能設(shè)置為多實(shí)例曹铃。

??按照BPMN2.0規(guī)范的要求缰趋,用于為每個(gè)實(shí)例創(chuàng)建執(zhí)行的父執(zhí)行,會(huì)提供下列變量:

  • nrOfInstances:實(shí)例總數(shù)陕见。
  • nrOfActiveInstances:當(dāng)前活動(dòng)的(即未完成的)實(shí)例數(shù)量秘血。對(duì)于順序多實(shí)例,這個(gè)值總為1评甜。
  • nrOfCompletedInstances:已完成的實(shí)例數(shù)量灰粮。

??可以調(diào)用execution.getVariable(x)方法獲取這些值。

??另外忍坷,每個(gè)被創(chuàng)建的執(zhí)行粘舟,都有局部變量(對(duì)其他執(zhí)行不可見(jiàn),也不存儲(chǔ)在流程實(shí)例級(jí)別):

  • loopCounter:給定實(shí)例在for-each循環(huán)中的index佩研。

1.7.2 圖示

??如果一個(gè)活動(dòng)是多實(shí)例柑肴,將通過(guò)在該活動(dòng)底部的三條短線(xiàn)表示。三條豎線(xiàn)代表實(shí)例會(huì)并行執(zhí)行韧骗,而三條橫線(xiàn)代表順序執(zhí)行。

多實(shí)例.png

1.7.3 XML表示

??要將活動(dòng)變成多實(shí)例零聚,該活動(dòng)的XML元素必須有multiInstanceLoopCharacteristics子元素

    <multiInstanceLoopCharacteristics isSequential="false|true">
     ...
    </multiInstanceLoopCharacteristics>

??isSequential屬性代表了活動(dòng)的實(shí)例為順序還是并行執(zhí)行袍暴。

??有4種不同方法可以配置數(shù)量。

1.7.3.1 指定數(shù)字

??通過(guò)loopCardinality子元素隶症,直接指定數(shù)字:

    <multiInstanceLoopCharacteristics isSequential="false|true">
      <loopCardinality>5</loopCardinality>
    </multiInstanceLoopCharacteristics>
1.7.3.2 表達(dá)式

??使用解析為正整數(shù)的表達(dá)式:

    <multiInstanceLoopCharacteristics isSequential="false|true">
      <loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality>
    </multiInstanceLoopCharacteristics>
1.7.3.3 指定集合

??另一個(gè)定義實(shí)例數(shù)量的方法政模,是使用loopDataInputRef子元素,指定一個(gè)集合型流程變量的名字蚂会。對(duì)集合中的每一項(xiàng)淋样,都會(huì)創(chuàng)建一個(gè)實(shí)例⌒沧。可以使用inputDataItem子元素趁猴,將該項(xiàng)設(shè)置給該實(shí)例的局部變量刊咳。在下面的XML示例中展示:

    <userTask id="miTasks" name="My Task ${loopCounter}" flowable:assignee="${assignee}">
      <multiInstanceLoopCharacteristics isSequential="false">
        <loopDataInputRef>assigneeList</loopDataInputRef>
        <inputDataItem name="assignee" />
      </multiInstanceLoopCharacteristics>
    </userTask>

??假設(shè)變量assigneeList包含[kermit, gonzo, fozzie]。上面的代碼會(huì)創(chuàng)建三個(gè)并行的用戶(hù)任務(wù)儡司。每一個(gè)執(zhí)行都有一個(gè)名為assignee的(局部)流程變量娱挨,含有集合中的一項(xiàng),并在這個(gè)例子中被用于指派用戶(hù)任務(wù)捕犬。

??loopDataInputRefinputDataItem的缺點(diǎn)是名字很難記跷坝,并且由于BPMN 2.0概要的限制,不能使用表達(dá)式碉碉。Flowable通過(guò)在multiInstanceCharacteristics上提供collectionelementVariable屬性解決了這些問(wèn)題:

    <userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
      <multiInstanceLoopCharacteristics isSequential="true"
         flowable:collection="${myService.resolveUsersForTask()}" flowable:elementVariable="assignee" >
      </multiInstanceLoopCharacteristics>
    </userTask>

??請(qǐng)注意collection屬性會(huì)作為表達(dá)式進(jìn)行解析柴钻。如果表達(dá)式解析為字符串而不是一個(gè)集合,不論是因?yàn)楸旧砼渲玫木褪庆o態(tài)字符串值垢粮,還是表達(dá)式計(jì)算結(jié)果為字符串贴届,這個(gè)字符串都會(huì)被當(dāng)做變量名,在流程變量中用于獲取實(shí)際的集合足丢。

??例如粱腻,下面的代碼片段會(huì)讓引擎查找存儲(chǔ)在assigneeList流程變量中的集合:

    <userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
      <multiInstanceLoopCharacteristics isSequential="true"
         flowable:collection="assigneeList" flowable:elementVariable="assignee" >
      </multiInstanceLoopCharacteristics>
    </userTask>
1.7.3.4 條件型數(shù)量

??多實(shí)例活動(dòng)在所有實(shí)例都完成時(shí)結(jié)束。然而斩跌,也可以指定一個(gè)表達(dá)式绍些,在每個(gè)實(shí)例結(jié)束時(shí)進(jìn)行計(jì)算。當(dāng)表達(dá)式計(jì)算為true時(shí)耀鸦,將銷(xiāo)毀所有剩余的實(shí)例柬批,并結(jié)束多實(shí)例活動(dòng),繼續(xù)執(zhí)行流程袖订。這個(gè)表達(dá)式必須通過(guò)completionCondition子元素定義氮帐。

    <userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
      <multiInstanceLoopCharacteristics isSequential="false"
         flowable:collection="assigneeList" flowable:elementVariable="assignee" >
        <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
      </multiInstanceLoopCharacteristics>
    </userTask>

??在這個(gè)例子里,會(huì)為assigneeList集合中的每個(gè)元素創(chuàng)建并行實(shí)例洛姑。當(dāng)60%的任務(wù)完成時(shí)上沐,其他的任務(wù)將被刪除,流程繼續(xù)運(yùn)行楞艾。

二参咙、集成擴(kuò)展的任務(wù)類(lèi)型

??Flowable還有很多集成擴(kuò)展型的任務(wù),這類(lèi)任務(wù)并不常用硫眯,初學(xué)讀者可以略過(guò)蕴侧,在需要時(shí)再回頭查閱。

2.1 手動(dòng)任務(wù)

2.1.1 描述

??手動(dòng)任務(wù)(manual task)用來(lái)定義在BPM引擎不能完成的任務(wù)两入。對(duì)于引擎來(lái)說(shuō)净宵,手動(dòng)任務(wù)將當(dāng)做一個(gè)空任務(wù)來(lái)處理,在流程執(zhí)行到達(dá)手動(dòng)任務(wù)時(shí),自動(dòng)繼續(xù)執(zhí)行流程择葡。

2.1.2 圖示

??手動(dòng)任務(wù)用左上角有一個(gè)小“手”圖標(biāo)的標(biāo)準(zhǔn)BPMN 2.0任務(wù)(圓角矩形)表示紧武。

手動(dòng)任務(wù).png

2.1.3 XML表示

    <manualTask id="myManualTask" name="Call client for more information" />

2.2 Java接收任務(wù)

2.2.1 描述

??接收任務(wù)(receive task),是等待特定消息到達(dá)的簡(jiǎn)單任務(wù)刁岸。當(dāng)流程執(zhí)行到達(dá)接收任務(wù)時(shí)脏里,將保持等待狀態(tài),直到引擎接收到特定的消息虹曙,觸發(fā)流程穿過(guò)接收任務(wù)繼續(xù)執(zhí)行迫横。

2.2.2 圖示

??接收任務(wù)用左上角有一個(gè)消息圖標(biāo)的標(biāo)準(zhǔn)BPMN 2.0任務(wù)(圓角矩形)表示。消息圖標(biāo)是白色的(對(duì)應(yīng)的黑色消息圖標(biāo)代表發(fā)送的含義)酝碳。

java接收任務(wù).png

2.2.3 XML表示

    <receiveTask id="waitState" name="wait" />

2.2.4 使用方法

??要使流程實(shí)例從接收任務(wù)的等待狀態(tài)中繼續(xù)執(zhí)行矾踱,需要使用到達(dá)接收任務(wù)的執(zhí)行id,調(diào)用runtimeService.signal(executionId)疏哗。下面的代碼片段展示了如何操作:

    ProcessInstance pi = runtimeService.startProcessInstanceByKey("receiveTask");
    Execution execution = runtimeService.createExecutionQuery()
      .processInstanceId(pi.getId())
      .activityId("waitState")
      .singleResult();

    runtimeService.trigger(execution.getId());

2.3 Shell任務(wù)

2.3.1 描述

??Shell任務(wù)(Shell task)可以運(yùn)行Shell腳本與命令呛讲。請(qǐng)注意Shell任務(wù)不是BPMN 2.0規(guī)范的“官方”任務(wù)(因此也沒(méi)有專(zhuān)用圖標(biāo))。

2.3.2 定義Shell任務(wù)

??Shell任務(wù)實(shí)現(xiàn)為特殊的服務(wù)任務(wù)返奉,將服務(wù)任務(wù)的type定義為'shell'進(jìn)行設(shè)置贝搁。

    <serviceTask id="shellEcho" flowable:type="shell">

2.3.3 Shell任務(wù)參數(shù)

??Shell任務(wù)通過(guò)字段注入配置。這些參數(shù)的值可以使用EL表達(dá)式芽偏,將在流程執(zhí)行運(yùn)行時(shí)解析雷逆。可以設(shè)置下列參數(shù):

參數(shù) 必填? 類(lèi)型 描述 默認(rèn)值
command String 要執(zhí)行的Shell命令污尉。
arg0-5 String 參數(shù)0至參數(shù)5
wait true/false 是否等待Shell進(jìn)程終止膀哲。 true
redirectError true/false 是否將標(biāo)準(zhǔn)錯(cuò)誤(standard error)并入標(biāo)準(zhǔn)輸出(standard output)。 false
cleanEnv true/false 是否避免Shell進(jìn)程繼承當(dāng)前環(huán)境被碗。 false
outputVariable String 保存輸出的變量名 不會(huì)記錄輸出某宪。
errorCodeVariable String 保存結(jié)果錯(cuò)誤碼的變量名 不會(huì)記錄錯(cuò)誤碼。
directory String Shell進(jìn)程的默認(rèn)目錄 當(dāng)前目錄

2.3.4 使用示例

??下面的XML代碼片段是使用Shell任務(wù)的例子锐朴。將會(huì)運(yùn)行"cmd /c echo EchoTest" Shell腳本兴喂,等待其結(jié)束,并將其結(jié)果存入resultVar焚志。

    <serviceTask id="shellEcho" flowable:type="shell" >
      <extensionElements>
        <flowable:field name="command" stringValue="cmd" />
        <flowable:field name="arg1" stringValue="/c" />
        <flowable:field name="arg2" stringValue="echo" />
        <flowable:field name="arg3" stringValue="EchoTest" />
        <flowable:field name="wait" stringValue="true" />
        <flowable:field name="outputVariable" stringValue="resultVar" />
      </extensionElements>
    </serviceTask>

2.4 補(bǔ)償處理器

2.4.1 描述

??如果要使用一個(gè)活動(dòng)補(bǔ)償另一個(gè)活動(dòng)的影響衣迷,可以將其聲明為補(bǔ)償處理器(compensation handler)。補(bǔ)償處理器不在正常流程中執(zhí)行娩嚼,而只在流程拋出補(bǔ)償事件時(shí)才會(huì)執(zhí)行蘑险。

??補(bǔ)償處理器不得有入口或出口順序流滴肿。

??補(bǔ)償處理器必須通過(guò)單向的連接岳悟,關(guān)聯(lián)一個(gè)補(bǔ)償邊界事件。

2.4.2 圖示

??如果一個(gè)活動(dòng)是補(bǔ)償處理器,則會(huì)在其下部中間顯示補(bǔ)償事件圖標(biāo)贵少。下面摘錄的流程圖展示了一個(gè)帶有補(bǔ)償邊界事件的服務(wù)任務(wù)呵俏,并關(guān)聯(lián)至一個(gè)補(bǔ)償處理器。請(qǐng)注意補(bǔ)償處理器圖標(biāo)顯示在"cancel hotel reservation(取消酒店預(yù)訂)"服務(wù)任務(wù)的下部中間滔灶。

補(bǔ)償處理器.png

2.4.3 XML表示

??要將一個(gè)活動(dòng)聲明為補(bǔ)償處理器普碎,需要將isForCompensation屬性設(shè)置為true:

    <serviceTask id="undoBookHotel" isForCompensation="true" flowable:class="...">
    </serviceTask>

2.5 集成類(lèi)任務(wù)

  • Web Service任務(wù):調(diào)用外部的Web Service資源。

  • 郵件任務(wù):用于發(fā)送郵件录平。

  • Http任務(wù):用于發(fā)出Http請(qǐng)求麻车。

  • Camel任務(wù):集成消息路由框架Camel。

  • Mule任務(wù):集成企業(yè)系統(tǒng)總線(xiàn)框架Mule斗这。

??上面的集成類(lèi)任務(wù)在后續(xù)篇章中會(huì)詳細(xì)介紹集成內(nèi)容动猬,此處了解即可。

三表箭、小結(jié)

??本篇介紹了BPMN2.0規(guī)范下及Flowable自定義擴(kuò)展的任務(wù)類(lèi)型赁咙,F(xiàn)lowable提供的多種任務(wù)類(lèi)型基本覆蓋企業(yè)應(yīng)用的需求。但還有不少問(wèn)題需要我們關(guān)注免钻,比如腳本任務(wù)中的腳本安全和多實(shí)例中的線(xiàn)程安全彼水。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市极舔,隨后出現(xiàn)的幾起案子凤覆,更是在濱河造成了極大的恐慌,老刑警劉巖姆怪,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件叛赚,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡稽揭,警方通過(guò)查閱死者的電腦和手機(jī)俺附,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)溪掀,“玉大人事镣,你說(shuō)我怎么就攤上這事【疚福” “怎么了璃哟?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)喊递。 經(jīng)常有香客問(wèn)我随闪,道長(zhǎng),這世上最難降的妖魔是什么骚勘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任铐伴,我火速辦了婚禮撮奏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘当宴。我一直安慰自己畜吊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布户矢。 她就那樣靜靜地躺著玲献,像睡著了一般。 火紅的嫁衣襯著肌膚如雪梯浪。 梳的紋絲不亂的頭發(fā)上捌年,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音挂洛,去河邊找鬼延窜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛抹锄,可吹牛的內(nèi)容都是我干的逆瑞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼伙单,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼获高!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起吻育,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤念秧,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后布疼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體摊趾,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年游两,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了砾层。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贱案,死狀恐怖肛炮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宝踪,我是刑警寧澤侨糟,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站瘩燥,受9級(jí)特大地震影響秕重,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厉膀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一溶耘、第九天 我趴在偏房一處隱蔽的房頂上張望套鹅。 院中可真熱鬧,春花似錦汰具、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至澜倦,卻和暖如春聚蝶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背藻治。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工碘勉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人桩卵。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓验靡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親雏节。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胜嗓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容