不了解工作流框架 Activiti 中的流程事件?這篇工作流流程元素詳解,帶你詳細分析工作流流程執行過程中的各種事件


自定義擴展

  • BPMN 2.0標准對於各方都是一個好東西
    • 用戶不用擔心會綁死在供應商提供的專有解決方案上
    • 框架,特別是activiti這樣的開源框架,可以提供相同功能,甚至是更好的實現,足以和大的供應商媲美
    • 按照BPMN 2.0標准,從大供應商的解決方案遷移到activiti只要經過一個簡單而平滑的過程
  • BPMN 2.0標准不好的一點是
    • 它常常是不同公司之間大量討論和妥協的結果
    • 作為開發者去閱讀流程定義的BPMN 2.0xml時,有時會感覺用這種結構和方法去做事太麻煩了
    • 因此activiti把簡化開發作為最優先的事情,使用一些被稱為Activiti BPMN擴展的功能,這些擴展是新的結構或方法來簡化對應的結構,並不屬於BPMN 2.0規范
  • 根據BPMN 2.0標准開發自定義擴展的注意點:
    • 自定義擴展的前提是總有簡單的方法轉換成標准方法. 所以使用自定義擴展時,可以及時撤銷自定義擴展
    • 當使用自定義擴展時 ,總會清楚的指明使用了新的XML元素,屬性... 比如會使用activiti:命名空間前綴
    • 擴展的目標是最終加入到下一版本的BPMN規范,或者至少可以引起對特定BPMN結構的討論

事件

  • 事件用來表明流程的生命周期中發生了什么事. 事件總是畫成一個圓圈
  • 在BPMN 2.0中,事件有兩大分類:捕獲(catching)事件觸發(throwing)事件:
    • 捕獲(catching): 當流程執行到事件,會等待被觸發.觸發的類型是由內部圖表或XML中的類型聲明來決定的.捕獲事件與觸發事件在顯示方面是根據內部圖表是否被填充來區分的(白色)
    • 觸發(throwing): 當流程執行到事件,會觸發一個事件.觸發的類型是由內部圖表或XML中的類型聲明來決定的.觸發事件與捕獲事件在顯示方面是根據內部圖表是否被填充來區分的(黑色)

事件定義

  • 事件定義決定了事件的語義. 如果沒有事件定義,這個事件就不做什么特別的事情.沒有設置事件定義的開始事件不會在啟動流程時做任何事情
  • 如果給開始事件添加了一個事件定義(比如定時器事件定義)我們就聲明了開始流程的事件類型(這時定時器事件監聽器會在某個時間被觸發)

定時器事件定義

  • 定時器事件是根據指定的時間觸發的事件
  • 定時器事件可以用於開始事件,中間事件和邊界事件
  • 定時器定義元素:
  • timeDate: 觸發事件的時間. 使用ISO8601格式指定的一個確定的時間:
<timerEventDefinition>
    <timeDate>2011-03-11T12:13:14</timeDate>
</timerEventDefinition>
  • timeDuration: 指定定時器之前要等待多長時間. timeDuration可以設置為timerEventDefinition的子元素,使用ISO8601規定的格式
<timerEventDefinition>
	<!--等待10天-->
    <timeDuration>P10D</timeDuration>
</timerEventDefinition>
  • timeCycle: 指定重復執行的間隔. 可以用來定期啟動流程實例,或為超時時間發送多個提醒.timeCycle元素可以使用兩種格式:
    • ISO8601標准的格式
    • cron表達式
<timerEventDefinition>
	<!--重復3次,每次間隔10小時-->
    <timeCycle>R3/PT10H</timeCycle>
</timerEventDefinition>
  • 從整點開始,每5分鍾執行一次:
0 0/5 * * * ?
  • 注意:
    • 第一個數字表示秒, 而不是像通常Unix cron中那樣表示分鍾
    • 重復的時間周期能更好的處理相對時間, 可以計算一些特定的時間點:用戶任務的開始時間
    • cron表達式可以處理絕對時間, 這對定時啟動事件特別有用
  • 在定時器事件定義中使用表達式,可以通過流程變量來影響那個定時器定義: 流程定義必須包含ISO8601(或者cron)格式的字符串,以匹配對應的時間類型
  <boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport">
     <timerEventDefinition>
        <timeDuration>${duration}</timeDuration>
     </timerEventDefinition>
  </boundaryEvent>

只有啟用job執行器之后,定時器才會被觸發.activiti.cfg.xml中的jobExecutorActivate需要設置為true, 默認job執行器是關閉的

錯誤事件定義

  • 錯誤事件是由指定錯誤觸發的
  • 注意:
    • BPMN錯誤與Java異常完全不一樣:
      • BPMN錯誤事件是為了對業務異常建模
      • Java異常是要用特定方式處理
  • 錯誤事件定義會引用一個error元素,引用相同error元素的錯誤事件處理器會捕獲這個錯誤
<endEvent id="myErrorEndEvent">
	<!--引用一個錯誤聲明-->
  <errorEventDefinition errorRef="myError" />
</endEvent>

信號事件定義

  • 信號事件會引用一個已命名的信號
  • 信號全局范圍的事件(廣播語義).會發送給所有激活的處理器
  • 信號事件定義使用signalEventDefinition元素 .signalRef屬性會引用definitions根節點里定義的signal子元素(signalEventDefinition引用相同的signal元素)
<!--流程實例,其中會拋出一個信號,並被中間事件捕獲-->
<definitions... >
        <!-- declaration of the signal -->
        <signal id="alertSignal" name="alert" />

        <process id="catchSignal">
                <intermediateThrowEvent id="throwSignalEvent" name="Alert">
                        <!-- signal event definition -->
                        <signalEventDefinition signalRef="alertSignal" />
                </intermediateThrowEvent>
                ...
                <intermediateCatchEvent id="catchSignalEvent" name="On Alert">
                        <!-- signal event definition -->
                        <signalEventDefinition signalRef="alertSignal" />
                </intermediateCatchEvent>
                ...
        </process>
</definitions>
觸發信號事件
  • 可以通過bpmn節點由流程實例觸發一個信號.也可以通過API觸發
  • org.activiti.engine.RuntimeService中的方法可以用來手工觸發一個信號:
RuntimeService.signalEventReceived(String signalName);
RuntimeService.signalEventReceived(String signalName, String executionId);
  • signalEventReceived(String signalName): 把信號發送給全局所有訂閱的處理(廣播語義)
  • signalEventReceived(String signalName, String executionId): 把信號發送給指定的執行
捕獲信號事件
  • 信號事件可以被中間信號事件或邊界信息事件捕獲
查詢信號事件的訂閱
  • 查詢所有訂閱特定信號事件的執行
 List<Execution> executions = runtimeService.createExecutionQuery()
      .signalEventSubscriptionName("alert")
      .list();
  • 使用signalEventReceived(String signalName, String executionId) 把信號發送給這些執行
信號事件范圍
  • 默認情況下,信號會在流程引擎范圍內進行廣播: 在一個流程實例中拋出一個信號事件,其他不同流程定義的流程實例都可以監聽到這個事件
  • 有時只要在同一個流程實例中響應這個信號事件:流程實例中的同步機制,如果兩個或更多活動是互斥的
  • 要想限制信號事件的范圍, 可以使用信號事件定義的scope屬性:
<signal id="alertSignal" name="alert" activiti:scope"processInstance"/>

默認情況下,scope的屬性為global

信號事件實例
  • 不同流程使用信號交互:
  • 流程在保險規則更新或改變時啟動.在修改被參與者處理時,會觸發一個信號,通知規則改變:
    在這里插入圖片描述
    這個事件會被所有相關的流程實例捕獲
  • 訂閱這個事件的流程實例:
    -
  • 信號事件是廣播給所有激活的處理器的
  • 在上面的例子中,所有流程實例都會接收到這個事件,這就是我們想要的.
  • 然而,有的情況下並不想要這種廣播行為,考慮下面的流程:
    在這里插入圖片描述
    上述流程描述的模式activiti並不支持.這種想法是:
    • 執行[do something]任務時出現的錯誤
    • 被邊界錯誤事件捕獲
    • 然后使用信號傳播給並發路徑上的分支
    • 進而中斷[do something inparallel]任務
  • 目前,activiti實際運行的結果與期望一致.信號會傳播給邊界事件並中斷任務.但是,根據信號的廣播含義,也會傳播給所有其他訂閱了信號事件的流程實例.所以,這就不是我們想要的結果
  • 注意:
    • 信號事件不會執行任何與特定流程實例的聯系
    • 如果只想把一個信息發給指定的流程實例,需要手工關聯,再使用 signalEventReceived(String signalName, String executionId) 和對應的查詢機制

消息事件定義

  • 消息事件會引用一個命名的消息,每個消息都有名稱和內容
  • 消息事件總會直接發送給一個接受者
  • 消息事件定義使用messageEventDefinition元素.messageRef屬性引用了definitions根節點下的一個message子元素:
<!--使用兩個消息事件的流程例子,開始事件和中間捕獲事件分別聲明和引用了兩個消息事件-->
<definitions id="definitions"
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:activiti="http://activiti.org/bpmn"
  targetNamespace="Examples"
  xmlns:tns="Examples">

  <message id="newInvoice" name="newInvoiceMessage" />
  <message id="payment" name="paymentMessage" />

  <process id="invoiceProcess">

    <startEvent id="messageStart" >
        <messageEventDefinition messageRef="newInvoice" />
    </startEvent>
    ...
    <intermediateCatchEvent id="paymentEvt" >
        <messageEventDefinition messageRef="payment" />
    </intermediateCatchEvent>
    ...
  </process>

</definitions>
觸發消息事件
  • 作為一個嵌入式的流程引擎,activiti不能真正接收一個消息
    • 這些環境相關,與平台相關的活動:比如連接到JMS(Java消息服務)隊列或主題或執行WebService或REST請求. 這個消息的接收是你要在應用或架構的一層實現的,流程引擎則內嵌其中
  • 在應用接收一個消息之后,必須決定如何處理它:
  • 如果消息應該觸發啟動一個新流程實例,在下面的RuntimeService的兩個方法中選擇一個執行:
ProcessInstance startProcessInstanceByMessage(String messageName);
ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object> processVariables);
ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey, Map<String, Object> processVariables);  

這些方法允許使用對應的消息系統流程實例

  • 如果消息需要被運行中的流程實例處理:
    • 首先要根據消息找到對應的流程實例
    • 然后觸發這個等待中的流程
  • RuntimeService提供了可以基於消息事件的訂閱來觸發流程繼續執行:
void messageEventReceived(String messageName, String executionId);
void messageEventReceived(String messageName, String executionId, HashMap<String, Object> processVariables);   
查詢消息事件訂閱
  • Activiti支持開始消息事件中間消息事件
  • 消息開始事件: 消息事件訂閱分配給一個特定的process definition. 這個消息訂閱可以使用ProcessDefinitionQuery查詢到
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
      .messageEventSubscription("newCallCenterBooking")
      .singleResult();

因為同時只能有一個流程定義關聯到消息的訂閱點,查詢總是返回0或一個結果.如果流程定義更新了,那么只有最新版本的流程定義會訂閱到消息事件上

  • 中間捕獲消息事件: 消息事件訂閱會分配給特定的執行,這個消息事件訂閱可以使用ExecutionQuery查詢到:
Execution execution = runtimeService.createExecutionQuery()
      .messageEventSubscriptionName("paymentReceived")
      .variableValueEquals("orderId", message.getOrderId())
      .singleResult();

這個查詢可以調用對應的查詢,通常是流程相關的信息 :最多只能有一個流程實例對應着orderId

消息事件實例
  • 使用兩個不同消息啟動的流程實例:
    在這里插入圖片描述
  • 消息事件可以用於流程需要不同的方式來區分開始事件時,最終會進入同樣的路徑

開始事件

  • 開始事件用來指明流程在哪里開始
  • 開始事件的類型(流程在接收事件時啟動,還是在指定時間啟動...),定義了流程如何啟動, 這通過事件中不同的小圖表來展示.在XML中,這些類型是通過聲明不同的子元素來區分
  • 開始事件都是捕獲事件: 最終這些事件都是一直等待着,直到對應的觸發時機出現
  • 在開始事件中,可以設置activiti特定屬性:
    • initiator: 當流程啟動時,把當前登錄的用戶保存到指定的變量名中:
    <startEvent id="request" activiti:initiator="initiator" />
    
    • 登錄的用戶必須使用IdentityService.setAuthenticatedUserId(String) 方法設置,並包含在try-finally代碼中:
 try {
 identityService.setAuthenticatedUserId("bono");
 runtimeService.startProcessInstanceByKey("someProcessKey");
} finally {
 identityService.setAuthenticatedUserId(null);
}

空開始事件

描述
  • 空開始事件技術上意味着沒有指定啟動流程實例的觸發條件
  • 引擎不能預計什么時候流程實例會啟動
  • 空開始事件用於: 當流程實例要通過API啟動的場景,通過調用startProcessInstanceByXXX方法
  • 子流程都有一個空開始事件
ProcessInstance processInstance = runtimeService.startProcessInstanceByXXX();
圖形標記
  • 空開始事件顯示成一個圓圈.沒有內部圖表,即沒有觸發類型
    在這里插入圖片描述
XML結構
  • 空開始事件的XML結構是普通的開始事件定義 ,沒有任何子元素
  • 其他開始事件類型都有一個子元素來聲明自己的類型
<startEvent id="start" name="my start event" />
空開始事件的自定義擴展
  • formKey: 引用用戶在啟動新流程實例時需要填寫的表單模板
<startEvent id="request" activiti:formKey="org/activiti/examples/taskforms/request.form" />

定時開始事件

描述
  • 定時開始事件用來在指定的時間創建流程實例: 可以同時用於只啟動一次的流程和應該在特定時間間隔啟動多次的流程
  • 注意:
    • 子流程不能使用定時開始事件
    • 定時開始事件在流程發布后就會開始計算時間
      • 不需要調用startProcessInstanceByXXX, 雖然也可以調用啟動流程的方法,但是那會導致調用startProcessInstanceByXXX時啟動過多的流程
    • 當包含定時開始事件的新版本流程部署時, 對應的上一個定時器就會被刪除. 這是因為通常不希望自動啟動舊版本流程的流程實例
圖形標記
  • 定時開始事件顯示為一個圓圈,內部是一個表:
    在這里插入圖片描述
XML內容
  • 定時開始事件的XML內容是普通開始事件的聲明,包含一個定時定義子元素
		<startEvent id="theStart">
            <timerEventDefinition>
            	<!--流程會啟動4次,每次間隔5分鍾,從2011年3月11日,12:13開始計時-->
                <timeCycle>R4/2011-03-11T12:13/PT5M</timeCycle>
            </timerEventDefinition>
        </startEvent>
		<startEvent id="theStart">
            <timerEventDefinition>
            	<!--流程會根據選中的時間啟動一次-->
                <timeDate>2011-03-11T12:13:14</timeDate>
            </timerEventDefinition>
        </startEvent>

消息開始事件

描述
  • 消息開始事件可以使用一個命名的消息來啟動流程實例,這樣可以使用消息名稱來選擇正確的開始事件
  • 在發布包含一個或多個消息開始事件的流程定義時:
    • 消息開始事件的名稱在給定流程定義中不能重復:
      • 流程定義不能包含多個名稱相同的消息開始事件
      • 如果兩個或以上消息開始事件應用了相同的事件
      • 或兩個或以上消息事件引用的消息名稱相同
      • activiti會在發布流程定義時拋出異常
    • 消息開始事件的名稱在所有已發布的流程定義中不能重復:
      • 如果一個或多個消息開始事件引用了相同名稱的消息
      • 而這個消息開始事件已經部署到不同的流程定義中
      • activiti就會在發布時拋出一個異常
    • 在發布新版本的流程定義時,之前訂閱的消息訂閱會被取消:
      • 如果新版本中沒有消息事件也會這樣處理
  • 啟動流程實例,消息開始事件可以使用RuntimeService中的方法來觸發:
ProcessInstance startProcessInstanceByMessage(String messageName);
ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object> processVariables);
ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey, Map<String, Object< processVariables);
  • 這里的messageNamemessageEventDefinitionmessageRef屬性引用的message元素的name屬性
  • 啟動流程實例時,要考慮以下因素:
    • 消息開始事件只支持頂級流程,消息開始事件不支持內嵌子流程
    • 如果流程定義有多個消息開始事件, runtimeService.startProcessInstanceByMessage(...) 會選擇對應的開始事件
    • 如果流程定義有多個消息開始事件和一個空開始事件. runtimeService.startProcessInstanceByKey(...)和 runtimeService.startProcessInstanceById(...)會使用空開始事件啟動流程實例
    • 如果流程定義有多個消息開始事件,但是沒有空開始事件, runtimeService.startProcessInstanceByKey(...)和 runtimeService.startProcessInstanceById(...)會拋出異常
    • 如果流程定義只有一個消息開始事件, runtimeService.startProcessInstanceByKey(...)和 runtimeService.startProcessInstanceById(...)會使用這個消息開始事件啟動流程實例
    • 如果流程被調用環節(callActivity)啟動,消息開始事件只支持如下情況:
      • 在消息開始事件以外,還有一個單獨的空開始事件
      • 流程只有一個消息開始事件,沒有空開始事件
圖形標記
  • 消息開始事件是一個圓圈,中間是一個消息事件圖標.圖標是白色未填充的,來表示捕獲(接收)行為
    -
XML內容
  • 消息開始事件的XML內容在普通開始事件中,包含一個messageEventDefinition子元素:
<definitions id="definitions"
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:activiti="http://activiti.org/bpmn"
  targetNamespace="Examples"
  xmlns:tns="Examples">

  <message id="newInvoice" name="newInvoiceMessage" />

  <process id="invoiceProcess">

    <startEvent id="messageStart" >
        <messageEventDefinition messageRef="tns:newInvoice" />
    </startEvent>
    ...
  </process>

</definitions>

信號開始事件

描述
  • 信號開始事件可以通過一個已命名的信號(signal)來啟動一個流程實例
    • 信號可以在流程實例內部使用中間信號拋出事務觸發
    • 也可以通過API(runtimService.signalEventReceivedXXX)觸發
    • 兩種情況下,所有流程實例中擁有相同名稱的signalStartEvent都會啟動
    • 兩種情況下,都可以選擇同步或異步的方式啟動流程實例
  • 必須向API傳入signalName, 這是signal元素的name屬性值,它會被signalEventDefinitionsignalRef屬性引用
圖形標記
  • 信號開始事件顯示為一個中間包含信號事件圖標的圓圈.標記是無填充的,表示捕獲(接收)行為
    在這里插入圖片描述
XML格式
  • signalStartEvent的XML格式是標准的startEvent聲明,其中包含一個signalEventDefinition子元素
 <signal id="theSignal" name="The Signal" />

    <process id="processWithSignalStart1">
        <startEvent id="theStart">
          <signalEventDefinition id="theSignalEventDefinition" signalRef="theSignal"  />
        </startEvent>
        <sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
        <userTask id="theTask" name="Task in process A" />
        <sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" />
        <endEvent id="theEnd" />
    </process>

錯誤開始事件

描述
  • 錯誤開始事件可以用來觸發一個事件子流程.錯誤開始事件不能用來啟動流程實例
  • 錯誤開始事件都是中斷事件
圖形標記
  • 錯誤開始事件是一個圓圈,包含一個錯誤事件標記.標記是白色未填充的,來表示捕獲(接收)行為
    在這里插入圖片描述
XML內容
  • 錯誤開始事件的XML內容是普通開始事件定義中,包含一個 errorEventDefinition子元素
<startEvent id="messageStart" >
        <errorEventDefinition errorRef="someError" />
</startEvent>

結束事件

  • 結束事件表示(子)流程(分支)的結束
  • 結束事件都是觸發事件:
    • 流程達到結束事件,會觸發一個結果
    • 結果的類型是通過事件的內部黑色圖標表示的
    • 在XML內容中,通過包含的子元素聲明

空結束事件

描述
  • 空結束事件意味着到達事件時不會指定拋出的結果
  • 引擎會直接結束當前執行的分支,不會做其他事情
圖形標記
  • 空結束事件是一個粗邊圓圈, 內部沒有小圖標(無結果類型)
    在這里插入圖片描述
XML內容
  • 空結束事件的XML內容是普通結束事件定義
  • 不包含子元素,其他結束事件類型都會包含聲明類型的子元素
<endEvent id="end" name="my end event" />

錯誤結束事件

描述
  • 當流程執行到錯誤結束事件 ,流程的當前分支就會結束,並拋出一個錯誤
  • 這個錯誤可以被對應的中間邊界錯誤事件捕獲.如果找不到匹配的邊界錯誤事件,就會拋出一個異常
圖形標記
  • 錯誤結束事件是一個標准的結束事件 -粗邊圓圈, 內部有錯誤圖標, 錯誤圖標是全黑的,表示觸發語法
    在這里插入圖片描述
XML內容
  • 錯誤結束事件的內容是一個錯誤事件, 子元素為errorEventDefinition
<endEvent id="myErrorEndEvent">
  <errorEventDefinition errorRef="myError" />
</endEvent>
  • errorRef屬性引用定義在流程外部的error元素:
<error id="myError" errorCode="123" />
...
<process id="myProcess">
...
  • error的errorCode用來查找匹配的捕獲邊界錯誤事件
  • 如果errorRef與任何error都不匹配,就會使用errorRef來作為errorCode的縮寫. 這是activiti特定的縮寫:
<error id="myError" errorCode="error123" />
...
<process id="myProcess">
...
  <endEvent id="myErrorEndEvent">
    <errorEventDefinition errorRef="myError" />
  </endEvent>

等同於

<endEvent id="myErrorEndEvent">
  <errorEventDefinition errorRef="error123" />
</endEvent>
  • 注意errorRef必須與BPMN 2.0格式相符,必須是一個合法的QName

取消結束事件

描述
  • 取消結束事件只能與BPMN事務子流程結合使用
  • 當到達取消結束事件時,會拋出取消事件 ,它必須被取消邊界事件捕獲
  • 取消邊界事件會取消事務,並觸發補償機制
圖形標記
  • 取消結束事件顯示為標准的結束事件-粗邊圓圈,包含一個取消圖標.取消圖標是全黑的,表示觸發語法
    在這里插入圖片描述
XML內容
  • 取消結束事件內容是一個結束事件, 包含cancelEventDefinition子元素
<endEvent id="myCancelEndEvent">
  <cancelEventDefinition />
</endEvent>

邊界事件

  • 邊界事件都是捕獲事件,它會附在一個環節上
  • 邊界事件是捕獲事件,不可能觸發事件:
    • 當節點運行時,事件會監聽對應的觸發類型
    • 當事件被捕獲,節點就會中斷,同時執行事件的后續連線
  • 邊界事件的定義方式都一樣:
<boundaryEvent id="myBoundaryEvent" attachedToRef="theActivity">
      <XXXEventDefinition/>
</boundaryEvent>
  • 邊界事件使用如下方式進行定義:
    • 唯一標識:流程范圍
    • 使用caught屬性引用事件的節點,注意邊界事件和它們附加的節點在同一級別上:比如,邊界事件不是包含在節點內的
    • 格式為XXXEventDefinition的XML子元素 (比如,TimerEventDefinition,ErrorEventDefinition...)定義了邊界事件的類型

定時邊界事件

描述
  • 定時邊界事件就是一個暫停等待警告的時鍾
  • 當流程執行到綁定了邊界事件的環節,會啟動一個定時器
  • 當定時器觸發時(一定時間之內),環節就會中斷,並沿着定時邊界事件的外出連線繼續執行
圖形標記
  • 定時邊界事件是一個標准的邊界事件(邊界上的一個圓圈),內部是一個定時器小圖標
    在這里插入圖片描述
XML內容
  • 定時器邊界任務定義是一個標准的邊界事件,指定類型的子元素是timerEventDefinition元素
<boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport">
   <timerEventDefinition>
    <timeDuration>PT4H</timeDuration>
  </timerEventDefinition>
</boundaryEvent>
  • 在流程圖中,圓圈邊線是虛線:
    在這里插入圖片描述
  • 經典應用場景: 發送通知郵件,但是不打斷正常流程的執行
    • 中斷和非中斷的事件還是有區別的
      • 默認是中斷事件
      • 非中斷事件的情況,不會中斷原始環節,那個環節還停留在原地
      • 對應的,會創建一個新分支,並沿着事件的流向繼續執行.在XML內容中,要把cancelActivity屬性設置為false
      <boundaryEvent id="escalationTimer" cancelActivity="false" attachedToRef="firstLineSupport"/>
      
  • 注意: 邊界定時事件只能在job執行器啟用時使用
    • 比如:把activiti.cfg.xml中的jobExecutorActivate設置為true,默認job執行器是禁用的
邊界事件的問題
  • 同步問題: 邊界事件后面不能有多條外出連線
  • 解決這個問題的方法是在一個連線后使用並發網關
    -

錯誤邊界事件

描述
  • 錯誤邊界事件: 節點邊界上的中間捕獲錯誤事件,會捕獲節點范圍內拋出的錯誤
  • 定義一個邊界錯誤事件,大多用於內嵌子流程或者調用節點
    • 對於子流程的情況,它會為所有內部的節點創建一個作用范圍
    • 錯誤是由錯誤結束事件拋出的.這個錯誤會傳遞給上層作用域,直到找到一個錯誤事件定義相匹配的邊界錯誤事件
  • 當捕獲了錯誤事件時 ,邊界任務綁定的節點就會銷毀,也會銷毀內部所有的執行分支(同步節點,內嵌子流程...).流程執行會繼續沿着邊界事件的外出連線繼續執行
圖形標記
  • 錯誤邊界事件顯示成一個普通的中間事件(圓圈內部有一個小圓圈)放在節點的標記上,內部有一個錯誤小圖標.錯誤小圖標是白色的,表示它是一個捕獲事件
    在這里插入圖片描述
XML內容
  • 邊界錯誤事件定義為普通的邊界事件:
<boundaryEvent id="catchError" attachedToRef="mySubProcess">
  <errorEventDefinition errorRef="myError"/>
</boundaryEvent>
  • 和錯誤結束事件一樣 ,errorRef引用了process元素外部的一個錯誤定義
<error id="myError" errorCode="123" />
...
<process id="myProcess">
...
  • errorCode用來匹配捕獲的錯誤:
    • 如果沒有設置errorRef,邊界錯誤事件會捕獲所有錯誤事件,無論錯誤的errorCode是什么
    • 如果設置了errorRef,並引用了一個已存在的錯誤,邊界事件就只捕獲錯誤代碼與之相同的錯誤
    • 如果設置了errorRef,但是BPMN 2.0中沒有定義錯誤,errorRef就會當做errorCode使用
錯誤邊界事件實例
  • 如何使用錯誤結束事件的流程實例
    • 當完成審核盈利這個用戶任務時,如果沒有提供足夠的信息,就會拋出錯誤
    • 錯誤會被子流程的邊界任務捕獲,所有回顧銷售子流程中的所有節點都會銷毀,即使審核客戶比率還沒有完成,並創建一個提供更多信息的用戶任務
      在這里插入圖片描述

信號邊界事件

描述
  • 節點邊界的中間捕獲信號,會捕獲信號定義引用的相同信號名的信號
  • 與其他事件(比如邊界錯誤事件)不同,邊界信號事件不只捕獲綁定方位的信號.信號事件是一個全局的范圍(廣播語義),就是說信號可以在任何地方觸發,即便是不同的流程實例
  • 和其他事件(比如邊界錯誤事件)不同 ,捕獲信號后,不會停止信號的傳播. 如果有兩個信號邊界事件,捕獲相同的信號事件,兩個邊界事件都會被觸發,即使在不同的流程實例中
圖形標記
  • 邊界信號事件顯示為普通的中間事件(圓圈里有個小圓圈),位置在節點的邊緣, 內部有一個信號小圖標.信號圖標是白色的(未填充),表示捕獲的意思
    在這里插入圖片描述
XML內容
  • 邊界信號事件定義為普通的邊界事件:
<boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true">
          <signalEventDefinition signalRef="alertSignal"/>
</boundaryEvent>

消息邊界事件

描述
  • 節點邊界上的中間捕獲消息, 根據引用的消息定義捕獲相同消息名稱的消息
圖形標記
  • 邊界消息事件顯示成一個普通的中間事件(圓圈里有個小圓圈),位於節點邊緣,內部是一個消息小圖標.消息圖標是白色(無填充),表示捕獲的意思
    在這里插入圖片描述
  • 注意: 邊界消息事件可能是非中斷(左側) 或者中斷(右側)
XML內容
  • 邊界消息事件定義為標准的邊界事件
<boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true">
          <messageEventDefinition messageRef="newCustomerMessage"/>
</boundaryEvent>

取消邊界事件

描述
  • 在事務性子流程的邊界上的中間捕獲取消
  • 事務取消時觸發,當取消邊界事件觸發時:
    • 首先中斷當前作用域的所有執行
    • 然后開始補償事務內的所有激活的補償邊界事件.補償是同步執行的:離開事務前,邊界事務會等待補償執行完畢
    • 當補償完成后,事務子流程會沿着取消邊界事務的外出連線繼續執行
  • 注意:
    • 每個事務子流程只能有一個取消邊界事件
    • 如果事務子流程包含內嵌子流程,補償只會觸發已經成功完成的子流程
    • 如果取消邊界子流程對應的事務子流程配置為多實例,如果一個實例觸發了取消,就會取消所有實例
圖形標記
  • 取消邊界事件顯示為了一個普通的中間事件(圓圈里套小圓圈),在節點的邊緣,內部是一個取消小圖標.取消圖標是白色(無填充),表明是捕獲的意思
    在這里插入圖片描述
XML內容
  • 取消邊界事件定義為普通邊界事件
<boundaryEvent id="boundary" attachedToRef="transaction" >
          <cancelEventDefinition />
</boundaryEvent>
  • 取消邊界事件都是中斷的,不需要使用cancelActivity屬性

補償邊界事件

描述
  • 節點邊界的中間捕獲補償
  • 用來設置一個節點的補償處理器
  • 補償邊界事件必須使用直接引用設置唯一的補償處理器
  • 補償邊界事件與其他邊界事件的策略不同:
    • 其他邊界事件(信號邊界事件)當到達關聯的節點就會被激活.離開節點時,就會掛起,對應的事件訂閱也會取消
    • 補償邊界事件在關聯的節點成功完成時激活,當補償事件觸發或對應流程實例結束時,事件訂閱才會刪除
  • 補償邊界事件遵循如下規則:
    • 補償觸發時,補償邊界事件對應的補償處理器會調用相同次數,根據它對應的節點的成功次數
    • 如果補償邊界事件關聯到多實例節點,補償事件會訂閱每個實例
    • 如果補償邊界事件關聯的節點中包含循環,補償事件會在每次節點執行時進行訂閱
    • 如果流程實例結束,訂閱的補償事件都會結束
  • 補償邊界事件不支持內嵌子流程
圖形標記
  • 補償邊界事件顯示為標准中間事件(圓圈里套圓圈),位於節點邊緣,內部有一個補償小圖標.補償圖標是白色的(無填充),表示捕獲的意思
  • 下圖演示使用無方向的關聯,為邊界事件設置補償處理器:
    在這里插入圖片描述
XML內容
  • 補償邊界事件定義為標准邊界事件
<boundaryEvent id="compensateBookHotelEvt" attachedToRef="bookHotel" >
          <compensateEventDefinition />
</boundaryEvent>

<association associationDirection="One" id="a1"  sourceRef="compensateBookHotelEvt" targetRef="undoBookHotel" />

<serviceTask id="undoBookHotel" isForCompensation="true" activiti:class="..." />
  • 補償邊界事件在節點成功完成后激活, 不支持cancelActivity屬性

中間捕獲事件

  • 所有中間捕獲事件都使用同樣的方式定義:
<intermediateCatchEvent id="myIntermediateCatchEvent" >
      <XXXEventDefinition/>
</intermediateCatchEvent>
  • 中間捕獲事件的定義:
    • 唯一標識(流程范圍內)
    • 一個結構為XXXEventDefinition的XML子元素(TimerEventDefinition)定義了中間捕獲事件的類型

定時中間捕獲事件

描述
  • 定時中間事件作為一個監聽器
  • 當執行到達捕獲事件節點,就會啟動一個定時器.當定時器觸發(比如,一段時間之后),流程就會沿着定時中間事件的外出節點繼續執行
圖形標記
  • 定時器中間事件顯示成標准中間捕獲事件, 內部是一個定時器小圖標:
    在這里插入圖片描述
XML內容
  • 定時器中間事件定義為標准中間捕獲事件. 指定類型的子元素為timerEventDefinition元素
		<intermediateCatchEvent id="timer">
            <timerEventDefinition>
                <timeDuration>PT5M</timeDuration>
            </timerEventDefinition>
        </intermediateCatchEvent>

信號中間捕獲事件

  • 中間捕獲信號事件,通過引用信號定義來捕獲相同信號名稱的信號
  • 信號中間捕獲事件與其它事件(比如錯誤事件)不同:
    • 信號不會在捕獲之后被消費
    • 如果有兩個激活的信號邊界事件捕獲相同的信號事件,兩個邊界事件都會被觸發,即便在不同的流程實例中

圖形標記

  • 中間信號捕獲事件顯示為一個普通的中間事件(圓圈套圓圈),內部有一個信號小圖標.信號小圖標是白色的(無填充),表示捕獲的意思
    在這里插入圖片描述
XML內容
  • 信號中間事件定義為普通的中間捕獲事件. 對應類型的子元素是signalEventDefinition元素
<intermediateCatchEvent id="signal">
        <signalEventDefinition signalRef="newCustomerSignal" />
</intermediateCatchEvent>

消息中間捕獲事件

描述
  • 中間捕獲消息事件,捕獲特定名稱的消息
圖形標記
  • 中間捕獲消息事件顯示為普通中間事件(圓圈套圓圈),內部是一個消息小圖標.消息圖標是白色的(無填充),表示捕獲的意思
    在這里插入圖片描述
XML內容
  • 消息中間事件定義為標准中間捕獲事件. 指定類型的子元素是messageEventDefinition元素
<intermediateCatchEvent id="message">
        <messageEventDefinition signalRef="newCustomerMessage" />
</intermediateCatchEvent>

內部觸發事件

  • 所有內部觸發事件的定義都是一樣的:
<intermediateThrowEvent id="myIntermediateThrowEvent" >
      <XXXEventDefinition/>
</intermediateThrowEvent>
  • 內部觸發事件定義包含:
    • 唯一標識(流程范圍)
    • 使用格式為XXXEventDefinition的XML子元素(比如signalEventDefinition等)定義中間觸發事件的類型
中間觸發空事件
  • 空中間觸發事件流程圖,用於表示流程中的某個狀態
    在這里插入圖片描述
  • 可以添加執行監聽器:
<intermediateThrowEvent id="noneEvent">
  <extensionElements>
    <activiti:executionListener class="org.activiti.engine.test.bpmn.event.IntermediateNoneEventTest$MyExecutionListener" event="start" />
  </extensionElements>
</intermediateThrowEvent>
  • 可以添加自己的代碼,把事件發送給BAM工具或DWH.引擎不會為這個事件做任何事情,它直接徑直通過

信號中間觸發事件

描述
  • 信號中間觸發事件為定義的信號拋出一個信號事件
  • 在activiti中,信號會廣播到所有激活的處理器中.信號可以通過同步和異步方式發布
    • 默認配置下,信號是同步發送的:
      • 拋出事件的流程實例會等到信號發送給所有捕獲流程實例才繼續執行
      • 捕獲流程實例也會在觸發流程實例的同一個事務中執行
      • 如果某個監聽流程出現了技術問題(拋出異常),所有相關的實例都會失敗
    • 信號也可以異步發送:
      • 會在到達拋出信號事件后決定哪些處理器是激活的
      • 對這些激活的處理器,會保存一個異步提醒消息(任務),並發送給jobExecutor
圖形標記
  • 中間信號觸發事件顯示為普通中間事件(圓圈套圓圈),內部又一個信號小圖標,信號圖標是黑色的(有填充),表示觸發的意思
    在這里插入圖片描述
XML內容
  • 消息中間事件定義為標准中間觸發事件. 指定類型的子元素是signalEventDefinition元素
<intermediateThrowEvent id="signal">
        <signalEventDefinition signalRef="newCustomerSignal" />
</intermediateThrowEvent>
  • 異步信號事件如下:
<intermediateThrowEvent id="signal">
        <signalEventDefinition signalRef="newCustomerSignal" activiti:async="true" />
</intermediateThrowEvent>

補償中間觸發事件

描述
  • 用來觸發補償
  • 觸發補償:
    • 補償可以由特定節點或包含補償事件的作用域觸發
    • 補償是通過分配給節點的補償處理器來完成的
      • 當補償由節點觸發,對應的補償處理器會根據節點成功完成的次數執行相同次數
      • 如果補償由當前作用域觸發,當前作用域的所有節點都會執行補償,也包含並發分支
      • 補償的觸發是繼承式的:
        • 如果執行補償的節點是子流程,補償會作用到子流程中包含的所有節點
        • 如果子流程是內嵌節點,補償會遞歸觸發
        • 補償不會傳播到流程的上層
        • 如果補償在子流程中觸發,不會傳播到子流程范圍外
        • bpmn規范定義,由節點觸發的流程只會作用到子流程同一級別
      • activiti的補償執行次序與流程執行順序相反: 最后完成的節點會最先執行補償
      • 補償中間觸發事件可以用來補償成功完成的事務性子流程
  • 注意:
    • 如果補償被一個包含子流程的作用域觸發,子流程還包含了關聯補償處理器的節點, 如果它已經成功完成了,補償只會傳播到子流程
    • 如果子流程中的節點也完成了,並關聯了補償處理器,如果子流程包含的這些節點還沒有完成,就不會執行補償處理器
      在這里插入圖片描述
      這個流程中,我們有兩個並發分支,一個分支是內嵌子流程,一個是使用信用卡節點.假設兩個分支都啟動了,第一個分支等待用戶完成審核預定任務.第二個分支執行使用信用卡節點, 並發生了一個錯誤,這導致取消預定事件,並觸發補償.這時,並發子流程還沒有結束,意味着補償事件不會傳播給子流程, 所以取消旅店預定這個補償處理器不會執行.如果用戶任務(就是內嵌子流程)在取消預定之前完成了,補償就會傳播給內嵌子流程
  • 流程變量:
    • 當補償內嵌子流程時,用來執行補償處理器的分支可以訪問子流程的本地流程實例,因為這時是子流程完成的分支
    • 為了實現這個功能,流程變量的快照會分配給分支(為執行子流程而創建的分支)有以下限制條件:
      • 補償處理器無法訪問子流程內部創建的,添加到同步分支的變量
      • 分配給分支的流程變量在繼承關系上層的(分配給流程實例的流程變量沒有包含在快照中):補償觸發時,補償處理器通過它們所在的地方訪問這些流程變量
      • 變量快照只用於內嵌子流程,不適用其他節點
 已知限制:
 1. waitForCompletion="false"還不支持。當補償使用中間觸發補償事件觸發時, 事件沒有等待,在補償成功結束后
 2. 補償自己由並發分支執行。並發分支的執行順序與被補償的節點完成次序相反。 未來activiti可能支持選項來順序執行補償
 3. 補償不會傳播給callActivity調用的子流程實例
圖形標記
  • 中間補償觸發事件顯示為標准中間事件(圓圈套圓圈),內部是一個補償小圖標.補償圖標是黑色的(有填充),表示觸發的意思
    在這里插入圖片描述
XML內容
  • 補償中間事件定義為普通的中間觸發事件. 對應類型的子元素是compensateEventDefinition元素
<intermediateThrowEvent id="throwCompensation">
        <compensateEventDefinition />
</intermediateThrowEvent>
  • 可選參數activityRef可以用來觸發特定作用域/節點的補償
<intermediateThrowEvent id="throwCompensation">
        <compensateEventDefinition activityRef="bookHotel" />
</intermediateThrowEvent>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM