Activiti 是輕量級 Java 工作流引擎,核心以 BPMN 2.0 為標準。本文基於 5.19 版本,通過一個「請假審批」流程實例,完整演示從流程定義部署、實例啟動、任務查詢與辦理、流程掛起/激活、查詢 API 使用、變量管理、表達式、單元測試到 Web 集成的全流程開發要點。
流程定義以 BPMN 2.0 XML 形式存儲,部署後持久化至資料庫(ACT_RE_* 表),引擎重啟不丟失。使用 RepositoryService 部署:
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment()
.addClasspathResource("org/activiti/test/VacationRequest.bpmn20.xml")
.deploy();
System.out.println("流程定義數量: " + repositoryService.createProcessDefinitionQuery().count());
部署後,XML 被解析為可執行的 Java 對象模型,包含開始事件、用戶任務、排他網關、順序流等元素。
流程定義與實例是一對多、靜態與動態的關系。使用 RuntimeService 通過流程定義 Key 啟動,並攜帶初始流程變量:
Mapvariables = new HashMap<>(); variables.put("employeeName", "Kermit"); variables.put("numberOfDays", 4); variables.put("vacationMotivation", "I'm really tired!"); RuntimeService runtimeService = processEngine.getRuntimeService(); ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables); System.out.println("流程實例 ID: " + processInstance.getId()); System.out.println("運行中實例數: " + runtimeService.createProcessInstanceQuery().count());
流程變量作用域覆蓋整個實例,也是任務間傳遞數據的橋樑。
流程啟動後,首個節點通常是用戶任務,需人工介入。使用 TaskService 查詢候選組任務:
TaskService taskService = processEngine.getTaskService(); Listtasks = taskService.createTaskQuery() .taskCandidateGroup("management") .list(); for (Task task : tasks) { System.out.println("待辦任務: " + task.getName() + " [ID: " + task.getId() + "]"); }
辦理任務時攜帶任務變量,驅動流程流轉(如審批通過/駁回):
Task task = tasks.get(0); MaptaskVars = new HashMap<>(); taskVars.put("vacationApproved", "false"); // 駁回 taskVars.put("managerMotivation", "We have a tight deadline!"); taskService.complete(task.getId(), taskVars);
引擎根據排他網關條件表達式 ${vacationApproved == 'false'} 走向「修改請假單」節點,形成循環或結束。
支持流程定義級和實例級掛起:
repositoryService.suspendProcessDefinitionByKey("vacationRequest"),後續無法啟動新實例,拋 ActivitiException。激活用 activateProcessDefinitionByKey。runtimeService.suspendProcessInstance(instanceId),實例凍結,任務無法辦理。激活用 activateProcessInstance。Activiti 提供鏈式查詢 API,邏輯為 AND 組合:
Listtasks = taskService.createTaskQuery() .taskAssignee("kermit") .processVariableValueEquals("orderId", "0815") .orderByDueDate().asc() .list();
複雜查詢(OR 條件、跨表)可用原生 SQL:
Listtasks = taskService.createNativeTaskQuery() .sql("SELECT * FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}") .parameter("taskName", "gonzoTask") .list();
變量分流程變量(實例級,存 ACT_RU_VARIABLE)和局部變量(執行/任務級,僅當前作用域可見)。
RuntimeService:setVariable(s)、setVariableLocal(s)(局部)。TaskService:getVariable(s)、getVariableLocal(s)、complete(taskId, variables) 會將任務變量合併到流程變量。Execution/Task 對象直接調用 get/setVariable。默認獲取變量會一次性加載所有並緩存。大量變量場景可用新增參數控制:
execution.getVariable("myVar", false); // fetchAllVariables=false,僅查指定變量
支持值表達式 ${myVar}、${bean.prop} 和方法表達式 ${printer.print()}、${bean.addOrder('name')}。內置對象:execution、task、authenticatedUserId。廣泛用於網關條件、監聽器、腳本任務、表單屬性等。
JUnit 4 + ActivitiRule(推薦):
public class VacationProcessTest {
@Rule
public ActivitiRule activitiRule = new ActivitiRule("activiti.cfg.xml");
@Test
@Deployment(resources = "vacation.bpmn20.xml")
public void testRejectFlow() {
RuntimeService rs = activitiRule.getRuntimeService();
TaskService ts = activitiRule.getTaskService();
Map vars = new HashMap<>();
vars.put("employeeName", "test");
vars.put("numberOfDays", 1);
vars.put("vacationMotivation", "test");
rs.startProcessInstanceByKey("vacationRequest", vars);
Task task = ts.createTaskQuery().taskCandidateGroup("management").singleResult();
assertNotNull(task);
Map completeVars = new HashMap<>();
completeVars.put("vacationApproved", "false");
completeVars.put("managerMotivation", "busy");
ts.complete(task.getId(), completeVars);
// 驗證流程走向「修改請假單」
Task adjustTask = ts.createTaskQuery().taskAssignee("test").singleResult();
assertEquals("修改休假單", adjustTask.getName());
}
}
@Deployment 自動部署同目錄下同名 .bpmn20.xml,測試結束自動清理數據。
引擎線程安全,Web 環境需在啟動時創建、關閉時銷毀。配置 ServletContextListener:
public class ProcessEngineListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
ProcessEngines.init(); // 載入默認 activiti.cfg.xml
}
public void contextDestroyed(ServletContextEvent sce) {
ProcessEngines.destroy();
}
}
關鍵點:Service 層返回帶延遲加載關聯的實體給 Web 層時,需保證 Hibernate Session 仍開啟(或 Activiti 命令上下文未關閉),否則訪問關聯數據拋異常。可用過濾器在請求開始開啟命令上下文、請求結束關閉,實現「Open Session in View」模式。
Activiti 核心服務(RepositoryService、RuntimeService、TaskService、HistoryService、ManagementService)覆蓋流程全生命周期。掌握流程變量作用域、查詢 API、表達式、測試框架與 Web 集成模式,即可構建健壯、可維護的業務流程系統。