jBPM(Java Business Process Management)是經典的開源工作流引擎。本文基於jBPM 3.2版本,使用MySQL數據庫,完整演示一個「員工填單→經理審批→財務審批→結束」的報銷流程,涵蓋環境搭建、流程定義、API調用、單元測試全鏈路。
| 概念 | 說明 | 運行時對應 |
|---|---|---|
| ProcessDefinition | 流程靜態定義:節點、轉移、業務邏輯 | 部署後存入數據庫 |
| ProcessInstance | 流程運行實例,含啟動/結束時間、變量等 | 每次啟動創建一個 |
| Task | 節點中定義的具體工作項 | 屬於ProcessDefinition |
| TaskInstance | 任務運行實態:創建/開始/結束時間、辦理人 | 屬於ProcessInstance,三種狀態:create/start/end |
| Actor | 參與者標識,僅需actorId,具體用戶解析由業務層完成 | 任務分配依據 |
| Token | 執行指針,指向當前活動節點,樹狀結構支持並發,唯一root token | 運行期核心概念 |
jbpm,字符集UTF-8<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///jbpm</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
建議顯式指定MySQLInnoDBDialect以支持事務與外鍵。
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="test">
<start-state name="start">
<transition to="fillForm"/>
</start-state>
<task-node name="fillForm">
<task name="填寫報銷單">
<assignment actor-id="employee"/>
</task>
<transition to="managerDecide"/>
</task-node>
<task-node name="managerDecide">
<task name="經理審批">
<assignment actor-id="manager"/>
</task>
<transition to="finaceAudit"/>
</task-node>
<task-node name="finaceAudit">
<task name="財務審批">
<assignment actor-id="finace"/>
</task>
<transition to="end"/>
</task-node>
<end-state name="end"/>
</process-definition>
特點:順序流、固定辦理人(actor-id)、無條件分支,適合入門理解基礎API。
src/
├── cn/senta/test/TestJBPM.java # JUnit測試類
├── cn/senta/bx/processdefinition.xml # 流程定義文件
└── hibernate.cfg.xml # Hibernate配置
lib/
├── jbpm-3.2.x.jar
├── hibernate-3.x.jar
├── mysql-connector-java-5.1.5-bin.jar
└── ... 其他依賴
工程目錄結構截圖
package cn.senta.test;
import java.util.List;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.db.GraphSession;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.taskmgmt.exe.TaskInstance;
import junit.framework.TestCase;
public class TestJBPM extends TestCase {
// 1. 創建表結構(僅首次運行)
public void testCreateSchema() {
JbpmConfiguration.getInstance().createSchema();
System.out.println("數據庫表創建成功!");
}
// 2. 部署流程定義
public void testDeployProcessDefinition() {
JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
try {
ProcessDefinition pd = ProcessDefinition.parseXmlResource(
"cn/senta/bx/processdefinition.xml"
);
ctx.deployProcessDefinition(pd);
} finally { ctx.close(); }
System.out.println("流程部署成功!");
}
// 3. 啟動流程實例
public void testStartProcessInstance() {
JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
try {
GraphSession gs = ctx.getGraphSession();
ProcessDefinition pd = gs.findLatestProcessDefinition("test");
ProcessInstance pi = new ProcessInstance(pd);
ctx.save(pi);
pi.signal(); // 從start-state推進到第一個task-node
} finally { ctx.close(); }
System.out.println("流程實例啟動成功!");
}
// 4. 獲取指定用戶任務列表
@SuppressWarnings("unchecked")
public void testGetTaskList() {
String actorId = "employee";
JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
try {
List<TaskInstance> tasks = ctx.getTaskList(actorId);
for (TaskInstance ti : tasks) {
System.out.printf("id=%d, name=%s, create=%s, start=%s, end=%s%n",
ti.getId(), ti.getName(), ti.getCreate(), ti.getStart(), ti.getEnd());
}
} finally { ctx.close(); }
System.out.println("任務列表獲取成功!");
}
// 5. 開始任務(設置開始時間、辦理人)
public void testStartTask() {
long taskId = 1; // 從上一步獲取
JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
try {
TaskInstance ti = ctx.getTaskInstance(taskId);
ti.start(); // 狀態變為start
ctx.save(ti);
} finally { ctx.close(); }
System.out.println("任務開始成功!");
}
// 6. 結束任務(觸發流程流轉至下一節點)
public void testEndTask() {
long taskId = 1;
JbpmContext ctx = JbpmConfiguration.getInstance().createJbpmContext();
try {
TaskInstance ti = ctx.getTaskInstance(taskId);
ti.end(); // 狀態變為end,token沿transition移動
ctx.save(ti);
} finally { ctx.close(); }
System.out.println("任務結束成功,流程自動流轉!");
}
}
JBPM_*系列表。signal()從start-state推進到fillForm任務節點,生成TaskInstance(狀態create)。fillForm對應的TaskInstance。start()設置開始時間、狀態變start。end()設置結束時間、狀態變end,token自動沿transition移動到managerDecide,生成經理的TaskInstance。實際業務中,start()與end()通常合併在「提交」一個動作中;此處拆分演示API語義。
| 問題 | 原因 | 解決 |
|---|---|---|
| 創建表報錯 | MySQL驅動版本不匹配 | 使用5.1.x及以上穩定版驅動 |
| 部署報解析錯誤 | XML命名空間或標簽拼寫錯誤 | 對照jPDL 3.2 XSD校驗,注意task-node/task/assignment層次 |
| 啟動實例無任務生成 | 未調用signal()或start-state無出口transition | 檢查流程定義與啟動代碼 |
| 任務列表為空 | actorId不匹配或任務已被領取/完成 | 確認assignment actor-id與查詢actorId一致 |
| end()後流程未流轉 | 目標節點無入口transition或條件不滿足 | 檢查transition配置,無條件transition無需guard |
AssignmentHandler接口,根據流程變量決定actorId。ContextInstance設置/獲取流程變量,實現報銷金額、事由等業務數據流轉。managerDecide節點添加decision節點,根據金額走不同審批路徑。ActionHandler實現郵件通知、日誌記錄等橫切邏輯。本文通過完整可運行的代碼示例,演示了jBPM3從建表、部署、啟動、任務查詢、處理到流轉的核心API鏈路。掌握JbpmContext、ProcessInstance、TaskInstance、Token四大運行時對象的交互關係,是進階學習jBPM複雜流程建模、持久化策略、集群部署的基石。雖然jBPM3已停止維護,但其核心設計思想(PDL、Token驅動、命令模式)對理解Activiti、Flowable、Camunda等新一代引擎仍有極高參考價值。