Activiti 流程

Activiti 流程

流程引擎

流程啟動,運行的具體環(huán)境粱挡。

創(chuàng)建流程引擎

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

創(chuàng)建流程引擎時层坠,會在classpath下搜索activiti.cfg.xml配置文件,并基于此文件進行構(gòu)建买猖。

如果沒有查詢到配置文件改橘,則會基于默認配置創(chuàng)建引擎。

可以通過編程的方式實現(xiàn)引擎配置玉控。

配置內(nèi)容包括: 數(shù)據(jù)庫飞主, 是否啟用Job執(zhí)行器,郵件服務(wù)器高诺,歷史存儲碌识,緩存配置,日志虱而,事件監(jiān)聽等筏餐。

流程引擎的服務(wù)

api.services.png
// 創(chuàng)建引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 從引擎中獲取各類服務(wù)
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();

所有的服務(wù)都是無狀態(tài)的。

Repository Service 提供了管理和控制發(fā)布包和流程的定義操作牡拇。如:部署流程定義魁瞪;查詢引擎中的已有發(fā)布包和流程定義穆律;暫停或激活發(fā)布包佩番;獲取發(fā)布包中的資源众旗,如xml文件或是流程圖片等。

Runtime Service 負責啟動一個流程頂?shù)男聦嵗宋贰τ诿總€流程定義來說贡歧,同一個時間內(nèi),可以有多個實例在執(zhí)行赋秀。runtime service還可以用于獲取和保存流程實例中的變量利朵。或是用于查詢流程實例猎莲,執(zhí)行實例绍弟,觸發(fā)實例等。

Task Service 任務(wù)相關(guān)的服務(wù)著洼。包含功能:查詢分配給用戶或用戶組的任務(wù)的信息樟遣;創(chuàng)建獨立運行于流程實例外的任務(wù);手段設(shè)置任務(wù)與用戶的關(guān)聯(lián)關(guān)系身笤;認領(lǐng)(claim)任務(wù)豹悬, 完成(complete)任務(wù)等。

Identity Service 負責管理(創(chuàng)建液荸,更新瞻佛,刪除,查詢...)群組和用戶娇钱。注意伤柄,activiti執(zhí)行時不會對用戶執(zhí)行檢查。任務(wù)可以分配給任何人文搂,無論這個用戶是否存在适刀。

Form Service 表單服務(wù)∶翰洌可選的笔喉。提供啟動表單和任務(wù)表單兩個概念。即在流程實例啟動前展示給用戶的疯兼,和完成任務(wù)時展示給用戶的兩種表單然遏。注意贫途,這是個可選服務(wù)吧彪,表單不一定需要嵌入到流程定義中。

History Service 歷史數(shù)據(jù)服務(wù)丢早。執(zhí)行流程時姨裸,引擎會保存如實例啟動時間秧倾,任務(wù)參與者,完成時間傀缩,執(zhí)行路徑等數(shù)據(jù)那先。Histroy Service通過查詢功能獲取這些數(shù)據(jù)。

Management Service 管理服務(wù)赡艰。提供查詢和管理異步操作的功能售淡。異步操作的用途包含定時器,延遲慷垮,暫停揖闸,激活等。

流程定義

流程定義的發(fā)布

編寫bpmn的xml文件料身,并通過使用Repository Service進行發(fā)布汤纸。

示例: 某個請假流程的定義xml

<?xml version="1.0" encoding="UTF-8" ?>

<!-- targetNamespace用于指定用戶自定義的類別category, 主要用于對流程實例進行分類 -->
<!-- 其等同 repositoryService.createDeployment().category("yourCatagory")...deploy(); -->
<definitions id="definitions"
             targetNamespace="http://activiti.org/bpmn20"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:activiti="http://activiti.org/bpmn">
  
  <!-- process 流程。 建議一個xml中只包含一個流程 -->
  <!-- id: 必要的標識屬性 -->
  <process id="vacationRequest" name="Vacation request">
    
    <!-- 事件芹血。 事件有多種類型:開始贮泞,結(jié)束,定時器幔烛,錯誤啃擦,邊境等等--> 
    <!-- startEvent 開始事件,流程的入口 -->
    <!-- activiti:initiator 初始化變量说贝。 這里使用流程變量employeeName記錄流程發(fā)起者的名字 -->
    <startEvent id="request" activiti:initiator="employeeName">
      <!-- 擴展议惰。這里是一個啟動表單form -->
      <extensionElements>
        <activiti:formProperty id="numberOfDays" name="Number of days" type="long" value="1" required="true"/>
        <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
        <activiti:formProperty id="vacationMotivation" name="Motivation" type="string" />
      </extensionElements>
    </startEvent>

    <!-- 順序流,用于說明流程的流向 -->
    <sequenceFlow id="flow1" sourceRef="request" targetRef="handleRequest" />

    <!-- 任務(wù). 包含多種類型: userTask, serviceTask, scriptTask等 -->
    <!-- 用戶任務(wù)乡恕。需要由人完成的任務(wù) -->
    <userTask id="handleRequest" name="Handle vacation request" >
      <!-- 說明文件言询。可以使用代碼 task.getDescription() 獲取 -->
      <documentation>
        <!-- ${} UEL表達式傲宜。 可以獲取到流程內(nèi)的變量 -->
        ${employeeName} would like to take ${numberOfDays} day(s) of vacation (Motivation: ${vacationMotivation}).
      </documentation>
      <extensionElements>
         <activiti:formProperty id="vacationApproved" name="Do you approve this vacation" type="enum" required="true">
          <activiti:value id="true" name="Approve" />
          <activiti:value id="false" name="Reject" />
        </activiti:formProperty>
        <activiti:formProperty id="managerMotivation" name="Motivation" type="string" />
      </extensionElements>
      <!-- 任務(wù)的執(zhí)行人或群組运杭。 這里是指定群組為management -->
      <potentialOwner>
        <resourceAssignmentExpression>
          <formalExpression>management</formalExpression>
        </resourceAssignmentExpression>
      </potentialOwner>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="handleRequest" targetRef="requestApprovedDecision" />
    
    <!-- 網(wǎng)關(guān)。包含類型: 排他函卒, 并行辆憔, 包含, 事件等 -->
    <!-- 排他網(wǎng)關(guān)。 是否同意請假? -->
    <exclusiveGateway id="requestApprovedDecision" name="Request approved?" />

    <!-- management同意請假后的流程 -->
    <sequenceFlow id="flow3" sourceRef="requestApprovedDecision" targetRef="sendApprovalMail">
      <conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'true'}</conditionExpression>
    </sequenceFlow>
    <task id="sendApprovalMail" name="Send confirmation e-mail" />
    <sequenceFlow id="flow4" sourceRef="sendApprovalMail" targetRef="theEnd1" />
    <!-- 結(jié)束事件, 請假成功劈愚。 -->
    <endEvent id="theEnd1" />
    
    <!-- management不同意請假后的流程-->
    <sequenceFlow id="flow5" sourceRef="requestApprovedDecision" targetRef="adjustVacationRequestTask">
      <conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'false'}</conditionExpression>
    </sequenceFlow>
    <userTask id="adjustVacationRequestTask" name="Adjust vacation request">
      <documentation>
        Your manager has disapproved your vacation request for ${numberOfDays} days.
        Reason: ${managerMotivation}
      </documentation>
      <extensionElements>
        <activiti:formProperty id="numberOfDays" name="Number of days" value="${numberOfDays}" type="long" required="true"/>
        <activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" value="${startDate}" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
        <activiti:formProperty id="vacationMotivation" name="Motivation" value="${vacationMotivation}" type="string" />
        <!-- 表單元素:是否再次發(fā)起請假舍咖? -->
        <activiti:formProperty id="resendRequest" name="Resend vacation request to manager?" type="enum" required="true">
          <activiti:value id="true" name="Yes" />
          <activiti:value id="false" name="No" />
        </activiti:formProperty>
      </extensionElements>
        <!-- 任務(wù)執(zhí)行人為發(fā)起流程的人 -->
      <humanPerformer>
        <resourceAssignmentExpression>
          <formalExpression>${employeeName}</formalExpression>
        </resourceAssignmentExpression>
      </humanPerformer>
    </userTask>
    <sequenceFlow id="flow6" sourceRef="adjustVacationRequestTask" targetRef="resendRequestDecision" />
    
    <!-- 排他網(wǎng)關(guān)。 是否再次發(fā)起請假流程? -->
    <exclusiveGateway id="resendRequestDecision" name="Resend request?" />

    <!-- 再次請假列荔, 流程回到management handleRequest處 -->
    <sequenceFlow id="flow7" sourceRef="resendRequestDecision" targetRef="handleRequest">
      <conditionExpression xsi:type="tFormalExpression">${resendRequest == 'true'}</conditionExpression>
    </sequenceFlow>

    <!-- 不再請假 -->
     <sequenceFlow id="flow8" sourceRef="resendRequestDecision" targetRef="theEnd2">
      <conditionExpression xsi:type="tFormalExpression">${resendRequest == 'false'}</conditionExpression>
    </sequenceFlow>
    <!-- 結(jié)束事件2。請假失敗 -->
    <endEvent id="theEnd2" />

  </process>

</definitions>

xml對應(yīng)的流程圖

api.vacationRequest.png

流程的部署

    repositoryService.createDeployment()
        .name("my-process-name")
        .addClasspathResource("org/activiti/myProcess.bpmn20.xml")
        .addClasspathResource("org/activiti/myProcess.png")
        .deploy();

如果發(fā)布使用的是bpmn文件,則改為addClassResource("org/activiti/myProcess.bpmn")即可绘沉。
使用這種方式時煎楣,沒有提供流程圖片文件。因此可以通過配置流程引擎自動生成圖片车伞。

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
    ...
    <property name="createDiagramOnDeploy" value="true" />
</bean>

通過API獲取流程定義的圖片資源

    ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey("yourProcessId").singleResult();
    String diagramResourceName = procDef.getDiagramResourceName();
    InputStream imageStream = 
            repositoryService.getResourceAsStream(procDef.getDeploymentId(), diagramResourceName);

流程定義的掛起與激活

// 掛起流程择懂。流程將無法新建實例,繼續(xù)執(zhí)行另玖,異步操作困曙。
repositoryService.suspendProcessDefinitionByKey("vacationRequest");
try {
  runtimeService.startProcessInstanceByKey("vacationRequest");
} catch (ActivitiException e) {
  e.printStackTrace(); // 無法再啟動該定義流程的實例
}
// 重新激活流程
repositoryService.activateProcessDefinitionByKey("vacationRequest");

流程實例

流程實例都共享一個流程定義。
在啟動流程實例之前谦去,必須保證流程定義已經(jīng)被發(fā)布赂弓。然后通過流程定義的ID啟動實例。(在流程模型中定義的ID哪轿,在activiti中對應(yīng)于key盈魁。如流程id,任務(wù)id等)

    ProcessInstance proInst = runtimeService.startProcessInstanceByKey("yourProcessId");

創(chuàng)建一個流程實例窃诉,首先會進入開始事件杨耙。

在開始事件之后,它會沿著所有的外出連線執(zhí)行飘痛,到達第一個任務(wù)珊膜。Activiti會把一個任務(wù)保存到數(shù)據(jù)庫中。這時宣脉,分配到這個任務(wù)的用戶或群組會被解析车柠,也會保存到數(shù)據(jù)庫里。

Activiti引擎會繼續(xù)執(zhí)行流程環(huán)節(jié)塑猖,直至遇到一個等待狀態(tài)竹祷,如用戶任務(wù)等。在等待狀態(tài)下羊苟,當前的流程實例的狀態(tài)會保存到數(shù)據(jù)庫中塑陵。直至用戶決定完成任務(wù)才能改變這個狀態(tài)。

之后引擎會繼續(xù)執(zhí)行蜡励,直至遇到下一個等待狀態(tài)令花,或是流程結(jié)束。

在引擎運行過程中凉倚,如果出現(xiàn)重啟或是崩潰情況兼都,流程狀態(tài)也會安全的保存在數(shù)據(jù)庫中。

流程的啟動

流程定義發(fā)布到引擎后稽寒,就可以基于它發(fā)起新的流程實例扮碧。如先前所說,基于同一個流程定義可以同時存在多個流程實例瓦胎。

// 表單元素
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employeeName", "Kermit");
variables.put("numberOfDays", new Integer(4));
variables.put("vacationMotivation", "I'm really tired!");

// 使用RuntimeService獲得與流程運行相關(guān)的信息
RuntimeService runtimeService = processEngine.getRuntimeService();
// 從流程引擎中查找一個定義為"vacationRequest"的流程并啟動
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables);

// Verify that we started a new process instance
Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());

流程中的任務(wù)的完成

// Fetch all tasks for the management group
TaskService taskService = processEngine.getTaskService();
// 查找當前未完成的且執(zhí)行用戶組是management的任務(wù)
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
  Log.info("Task available: " + task.getName());
}

// 此處應(yīng)獲取到任務(wù): adjustVacationRequestTask
Task task = tasks.get(0);

Map<String, Object> taskVariables = new HashMap<String, Object>();
taskVariables.put("vacationApproved", "false"); //拒絕了請假
taskVariables.put("managerMotivation", "We have a tight deadline!");
// 任務(wù)完成芬萍。流程進入到下一個步驟
taskService.complete(task.getId(), taskVariables);

// !!如何需要獲取流程中的變量
runtimeService.getVariables(processInstanceId);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市搔啊,隨后出現(xiàn)的幾起案子柬祠,更是在濱河造成了極大的恐慌,老刑警劉巖负芋,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漫蛔,死亡現(xiàn)場離奇詭異,居然都是意外死亡旧蛾,警方通過查閱死者的電腦和手機莽龟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锨天,“玉大人毯盈,你說我怎么就攤上這事〔“溃” “怎么了搂赋?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長益缠。 經(jīng)常有香客問我脑奠,道長,這世上最難降的妖魔是什么幅慌? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任宋欺,我火速辦了婚禮,結(jié)果婚禮上胰伍,老公的妹妹穿的比我還像新娘齿诞。我一直安慰自己,他們只是感情好骂租,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布掌挚。 她就那樣靜靜地躺著,像睡著了一般菩咨。 火紅的嫁衣襯著肌膚如雪吠式。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天抽米,我揣著相機與錄音特占,去河邊找鬼。 笑死云茸,一個胖子當著我的面吹牛是目,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播标捺,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼懊纳,長吁一口氣:“原來是場噩夢啊……” “哼揉抵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嗤疯,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤冤今,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后茂缚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體戏罢,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年脚囊,在試婚紗的時候發(fā)現(xiàn)自己被綠了龟糕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡悔耘,死狀恐怖讲岁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衬以,我是刑警寧澤催首,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站泄鹏,受9級特大地震影響郎任,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜备籽,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一舶治、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧车猬,春花似錦霉猛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至伏嗜,卻和暖如春坛悉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背承绸。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工裸影, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人军熏。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓轩猩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子均践,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

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