引言
在现代企业级应用开发中,复杂的业务流程无处不在,例如请假审批、订单处理、合同审核等。这些流程往往涉及多个环节、多个角色以及各种条件分支。如果将这些流程逻辑硬编码在业务代码中,会导致代码臃肿、难以维护,且流程的任何变更都需要开发人员修改代码并重新部署。
Activity工作流引擎 正是为了解决这一问题而生的。它允许我们将业务过程的描述(先做什么,后做什么,由谁做,条件是什么)与具体的业务代码分离开来,从而实现业务流程的自动化管理。
一、Activity工作流是什么?
Activity是一个轻量级、开源的工作流和业务流程管理(BPM)引擎。它的核心是 BPMN 2.0 标准。
- 工作流引擎:可以理解为一个"业务流程的控制器"。它根据预先定义好的流程规则,推动一个流程实例从开始节点一步步执行到结束节点。
- BPMN 2.0:是一种全球通用的流程建模符号标准。它使用一套标准的图形元素(如圆圈、矩形、菱形)来描绘业务流程,使得业务分析师和开发人员可以用同一种"语言"进行沟通。
简单来说,你用BPMN 2.0的图表来"画"出流程,Activity引擎则负责"执行"你画出的流程图。
二、核心概念与架构
在深入代码之前,我们先了解几个核心概念,并通过一张架构图来建立整体认知。
| 概念 | 解释 | 类比 |
|---|---|---|
| ProcessEngine | 工作流引擎的核心,相当于整个系统的"大脑",所有服务都由它创建和管理。 | 公司的总指挥部 |
| RepositoryService | 负责管理流程定义(如部署一个BPMN图表文件)。 | 仓库管理员 |
| RuntimeService | 负责启动流程实例和管理运行中的流程。 | 流程启动器与监控中心 |
| TaskService | 负责管理流程中产生的"任务",例如查询任务、完成任务等。这是与用户交互最频繁的服务。 | 任务分发与回收站 |
| HistoryService | 负责查询历史流程实例信息,便于追踪和审计。 | 档案馆 |
| IdentityService | 负责管理用户和组(角色)。 | 人力资源部 |
Activity系统架构简图
这张图清晰地展示了Activity内部的核心协作关系:通过RepositoryService部署流程定义,然后由RuntimeService创建流程实例,TaskService则负责推动实例中具体任务的生命周期。
三、一个生动的例子:请假流程
让我们通过一个简单的请假流程来感受Activity的魅力。
1. 流程设计
假设一个请假流程是:员工申请 -> 经理审批 -> 结束。
我们用BPMN图来设计它:
- 开始事件:流程的起点。
- 用户任务:需要人工参与的活动,这里是"员工申请"和"经理审批"。
- 排他网关:像一个路标,根据条件决定流程的走向。例如,审批"批准"则结束,"拒绝"则打回重填。
- 结束事件:流程的终点。
2. 代码实现
首先,我们需要创建流程引擎并部署流程定义:
java
// 1. 创建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2. 获取RepositoryService,进行部署
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/leave-application.bpmn20.xml") // BPMN文件路径
.name("请假流程部署")
.deploy(); // 执行部署
System.out.println("流程部署ID: " + deployment.getId());
部署成功后,启动一个具体的请假流程实例:
java
// 3. 获取RuntimeService,启动流程实例
RuntimeService runtimeService = processEngine.getRuntimeService();
// 使用流程定义的Key来启动,默认是最新版本
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey("leaveApplication");
System.out.println("流程实例ID: " + processInstance.getId());
流程启动后,查询并完成任务:
java
// 4. 获取TaskService,查询任务
TaskService taskService = processEngine.getTaskService();
// 假设当前用户是"zhangsan"
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("zhangsan")
.list();
for (Task task : tasks) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
// 办理任务(填写请假单)
Map<String, Object> variables = new HashMap<>();
variables.put("leaveDays", 3); // 设置流程变量:请假3天
variables.put("reason", "回家探亲");
taskService.complete(task.getId(), variables); // 完成任务
}
经理审批任务:
java
// 经理登录系统,查询自己的待办任务
List<Task> managerTasks = taskService.createTaskQuery()
.taskAssignee("manager")
.list();
for (Task task : managerTasks) {
// 经理审批,传递审批意见
Map<String, Object> variables = new HashMap<>();
variables.put("approval", "reject"); // 审批结果:拒绝
taskService.complete(task.getId(), variables);
}
四、进阶特性:驾驭复杂业务流程
当业务变得复杂时,基础的单线流程就不够用了。Activity提供了强大的BPMN元素来应对。
1. 并行网关:处理并行任务
场景 :请假流程升级,需要员工直属经理和部门总监同时审批。
- 并行网关 :所有外出连线上的任务会同时被创建 ,并且必须全部完成,流程才会继续向下执行。直属经理和总监可以同时审批,互不依赖。
2. 包含网关:处理条件并行
场景:根据请假天数决定审批路径,3天以内只需直属经理审批,3-5天需要直属经理和HR审批,5天以上需要直属经理、HR和部门总监审批。
3. 子流程:实现流程模块化
场景:经理审批通过后,如果请假天数超过5天,需要启动一个"HR备案"子流程。
- 嵌入子流程:将一部分流程逻辑封装起来,使主流程更清晰。子流程可以有自己独立的作用域和变量。
4. 边界事件:响应异常与超时
场景:给经理审批任务加上一个"定时边界事件",如果48小时内未处理,则自动超时并通过邮件提醒经理。
边界事件详细说明:
- 当任务"经理审批"创建时,定时器开始计时(如48小时)
- 如果在时间内完成任务,流程正常继续
- 如果超时未完成,边界事件触发,执行"发送提醒邮件"
- 邮件发送后,可以重新分配任务或提醒经理处理
五、与Spring Boot无缝集成
现代Java应用开发离不开Spring Boot。Activity与Spring Boot的集成非常简单。
1. 添加依赖
XML
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.0.0.SR1</version>
</dependency>
2. 配置数据源(application.yml)
java
spring:
datasource:
url: jdbc:mysql://your-db-host:3306/activiti?useUnicode=true
username: your-username
password: your-password
activiti:
# 自动部署:检查resources/processes下的流程文件
check-process-definitions: true
database-schema-update: true
history-level: audit
3. 在Service层中使用
集成后,Activity的各个Service(如RuntimeService, TaskService)会作为Bean被Spring管理,可以直接@Autowired注入使用。
java
@Service
@Transactional
public class LeaveApplicationService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
/**
* 启动请假流程
*/
public ProcessInstance startLeaveProcess(LeaveRequest request) {
Map<String, Object> variables = new HashMap<>();
variables.put("employee", request.getEmployee());
variables.put("days", request.getDays());
variables.put("reason", request.getReason());
variables.put("startDate", request.getStartDate());
variables.put("endDate", request.getEndDate());
// 启动流程实例,并设置初始变量
ProcessInstance instance = runtimeService.startProcessInstanceByKey(
"leaveProcess",
request.getId().toString(), // 业务键
variables
);
return instance;
}
/**
* 查询用户待办任务
*/
public List<Task> getTasksForUser(String userId) {
return taskService.createTaskQuery()
.taskAssignee(userId)
.orderByTaskCreateTime().desc()
.list();
}
/**
* 完成任务
*/
public void completeTask(String taskId, Map<String, Object> variables) {
if (variables != null && !variables.isEmpty()) {
taskService.complete(taskId, variables);
} else {
taskService.complete(taskId);
}
}
/**
* 查询流程历史
*/
public List<HistoricProcessInstance> getProcessHistory(String businessKey) {
return historyService.createHistoricProcessInstanceQuery()
.processInstanceBusinessKey(businessKey)
.orderByProcessInstanceStartTime().desc()
.list();
}
}
4. RESTful API控制器
java
@RestController
@RequestMapping("/api/process")
public class ProcessController {
@Autowired
private LeaveApplicationService leaveApplicationService;
/**
* 启动请假流程
*/
@PostMapping("/leave/start")
public ResponseEntity<?> startLeaveProcess(@RequestBody LeaveRequest request) {
try {
ProcessInstance instance = leaveApplicationService.startLeaveProcess(request);
return ResponseEntity.ok(instance);
} catch (Exception e) {
return ResponseEntity.badRequest().body("启动流程失败: " + e.getMessage());
}
}
/**
* 获取用户待办任务
*/
@GetMapping("/tasks/{userId}")
public ResponseEntity<List<Task>> getUserTasks(@PathVariable String userId) {
List<Task> tasks = leaveApplicationService.getTasksForUser(userId);
return ResponseEntity.ok(tasks);
}
/**
* 完成任务
*/
@PostMapping("/tasks/{taskId}/complete")
public ResponseEntity<?> completeTask(@PathVariable String taskId,
@RequestBody Map<String, Object> variables) {
try {
leaveApplicationService.completeTask(taskId, variables);
return ResponseEntity.ok("任务完成成功");
} catch (Exception e) {
return ResponseEntity.badRequest().body("完成任务失败: " + e.getMessage());
}
}
}
六、精细控制:流程变量与监听器
流程变量
流程变量是贯穿流程实例生命周期的上下文数据,用于传递业务参数和控制流程走向。
java
/**
* 流程变量管理示例
*/
@Service
public class ProcessVariableService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
/**
* 设置流程变量
*/
public void setProcessVariables(String executionId, Map<String, Object> variables) {
runtimeService.setVariables(executionId, variables);
}
/**
* 获取流程变量
*/
public Object getProcessVariable(String executionId, String variableName) {
return runtimeService.getVariable(executionId, variableName);
}
/**
* 设置任务局部变量
*/
public void setTaskLocalVariables(String taskId, Map<String, Object> variables) {
taskService.setVariablesLocal(taskId, variables);
}
/**
* 在完成任务时设置变量
*/
public void completeTaskWithVariables(String taskId, Map<String, Object> variables) {
// 这些变量会在任务完成后成为流程变量
taskService.complete(taskId, variables);
}
}
监听器
监听器允许你在流程的特定点(如任务创建、任务完成、流程开始/结束)插入自定义逻辑。
执行监听器架构
1. 任务监听器
java
/**
* 任务分配监听器 - 在任务创建时自动发送通知
*/
@Component
public class TaskAssignmentListener implements TaskListener {
@Autowired
private NotificationService notificationService;
@Autowired
private UserService userService;
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
String assignee = delegateTask.getAssignee();
String taskName = delegateTask.getName();
String processInstanceId = delegateTask.getProcessInstanceId();
switch (eventName) {
case "create":
handleTaskCreate(delegateTask, assignee, taskName, processInstanceId);
break;
case "complete":
handleTaskComplete(delegateTask, assignee, taskName, processInstanceId);
break;
case "assignment":
handleTaskAssignment(delegateTask, assignee, taskName, processInstanceId);
break;
}
}
private void handleTaskCreate(DelegateTask delegateTask, String assignee,
String taskName, String processInstanceId) {
if (assignee != null) {
// 发送通知
String message = String.format("您有新的待办任务:%s (流程实例ID: %s)",
taskName, processInstanceId);
notificationService.sendNotification(assignee, message);
// 记录日志
System.out.println("任务创建通知已发送给: " + assignee);
}
}
private void handleTaskComplete(DelegateTask delegateTask, String assignee,
String taskName, String processInstanceId) {
// 任务完成时的处理逻辑
String completionMessage = String.format("任务已完成:%s (办理人: %s)",
taskName, assignee);
System.out.println(completionMessage);
// 可以记录操作日志到数据库
// auditService.logTaskCompletion(taskName, assignee, new Date());
}
private void handleTaskAssignment(DelegateTask delegateTask, String assignee,
String taskName, String processInstanceId) {
// 任务分配时的处理逻辑
System.out.println("任务已分配给: " + assignee);
}
}
2. 执行监听器
java
/**
* 流程执行监听器 - 监听流程开始、结束等事件
*/
@Component
public class ProcessExecutionListener implements ExecutionListener {
@Autowired
private BusinessService businessService;
@Autowired
private AuditService auditService;
@Override
public void notify(DelegateExecution execution) {
String eventName = execution.getEventName();
String processInstanceId = execution.getProcessInstanceId();
String activityId = execution.getCurrentActivityId();
switch (eventName) {
case "start":
handleProcessStart(execution, processInstanceId);
break;
case "end":
handleProcessEnd(execution, processInstanceId);
break;
case "take":
handleTransition(execution, processInstanceId, activityId);
break;
}
}
private void handleProcessStart(DelegateExecution execution, String processInstanceId) {
String businessKey = execution.getProcessInstanceBusinessKey();
System.out.println("流程启动: " + processInstanceId + ", 业务键: " + businessKey);
if (businessKey != null) {
// 更新业务实体状态为"审批中"
businessService.updateStatus(businessKey, "IN_PROGRESS");
}
// 记录流程启动审计日志
auditService.logProcessStart(processInstanceId, businessKey, new Date());
}
private void handleProcessEnd(DelegateExecution execution, String processInstanceId) {
String businessKey = execution.getProcessInstanceBusinessKey();
String status = (String) execution.getVariable("finalStatus");
System.out.println("流程结束: " + processInstanceId + ", 最终状态: " + status);
if (businessKey != null) {
// 更新业务实体状态
businessService.updateStatus(businessKey, status != null ? status : "COMPLETED");
}
// 记录流程结束审计日志
auditService.logProcessEnd(processInstanceId, businessKey, status, new Date());
}
private void handleTransition(DelegateExecution execution, String processInstanceId, String activityId) {
// 记录流程流转日志
System.out.println("流程流转至: " + activityId);
auditService.logActivityTransition(processInstanceId, activityId, new Date());
}
}
3. 在BPMN XML中配置监听器
XML
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="http://www.activiti.org/processdef">
<process id="complexLeaveProcess" name="复杂请假流程" isExecutable="true">
<!-- 开始事件 -->
<startEvent id="startEvent" name="开始">
<extensionElements>
<activiti:executionListener class="com.example.ProcessExecutionListener" event="start" />
</extensionElements>
</startEvent>
<!-- 员工申请任务 -->
<userTask id="employeeApply" name="员工申请" activiti:assignee="${applicant}">
<extensionElements>
<activiti:taskListener event="create" class="com.example.TaskAssignmentListener" />
<activiti:taskListener event="complete" class="com.example.TaskAssignmentListener" />
</extensionElements>
</userTask>
<!-- 经理审批任务 -->
<userTask id="managerApprove" name="经理审批" activiti:assignee="manager">
<extensionElements>
<activiti:taskListener event="create" class="com.example.TaskAssignmentListener" />
<activiti:taskListener event="complete" class="com.example.TaskAssignmentListener" />
</extensionElements>
</userTask>
<!-- 并行网关 -->
<parallelGateway id="parallelGateway1" name="并行审批"></parallelGateway>
<parallelGateway id="parallelGateway2" name="汇聚结果"></parallelGateway>
<!-- HR审批任务 -->
<userTask id="hrApprove" name="HR审批" activiti:assignee="hr"></userTask>
<!-- 总监审批任务 -->
<userTask id="directorApprove" name="总监审批" activiti:assignee="director"></userTask>
<!-- 排他网关 -->
<exclusiveGateway id="exclusiveGateway1" name="审批结果判断"></exclusiveGateway>
<!-- 结束事件 -->
<endEvent id="endEvent" name="结束">
<extensionElements>
<activiti:executionListener class="com.example.ProcessExecutionListener" event="end" />
</extensionElements>
</endEvent>
<!-- 序列流连接 -->
<sequenceFlow id="flow1" sourceRef="startEvent" targetRef="employeeApply" />
<sequenceFlow id="flow2" sourceRef="employeeApply" targetRef="exclusiveGateway1" />
<sequenceFlow id="flow3" sourceRef="exclusiveGateway1" targetRef="managerApprove">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${days <= 3}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" sourceRef="exclusiveGateway1" targetRef="parallelGateway1">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${days > 3}]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="parallelGateway1" targetRef="hrApprove" />
<sequenceFlow id="flow6" sourceRef="parallelGateway1" targetRef="directorApprove" />
<sequenceFlow id="flow7" sourceRef="hrApprove" targetRef="parallelGateway2" />
<sequenceFlow id="flow8" sourceRef="directorApprove" targetRef="parallelGateway2" />
<sequenceFlow id="flow9" sourceRef="managerApprove" targetRef="parallelGateway2" />
<sequenceFlow id="flow10" sourceRef="parallelGateway2" targetRef="endEvent" />
</process>
</definitions>
七、完整示例:复杂请假流程实现
下面是一个完整的复杂请假流程实现,包含所有讨论的特性:
java
/**
* 复杂请假流程服务
*/
@Service
@Transactional
public class ComplexLeaveProcessService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private UserService userService;
/**
* 部署流程定义
*/
public Deployment deployProcess() {
return repositoryService.createDeployment()
.addClasspathResource("processes/complex-leave-process.bpmn20.xml")
.name("复杂请假流程")
.deploy();
}
/**
* 启动复杂请假流程
*/
public ProcessInstance startComplexLeaveProcess(ComplexLeaveRequest request) {
// 确定审批人
String manager = userService.findManagerByEmployee(request.getEmployeeId());
String hr = userService.findHROfficer();
String director = userService.findDepartmentDirector(request.getDepartmentId());
Map<String, Object> variables = new HashMap<>();
variables.put("applicant", request.getEmployeeId());
variables.put("manager", manager);
variables.put("hr", hr);
variables.put("director", director);
variables.put("days", request.getDays());
variables.put("reason", request.getReason());
variables.put("startDate", request.getStartDate());
variables.put("endDate", request.getEndDate());
variables.put("department", request.getDepartmentId());
// 根据天数设置审批路径
if (request.getDays() > 5) {
variables.put("needDirectorApprove", true);
} else if (request.getDays() > 3) {
variables.put("needHRApprove", true);
}
return runtimeService.startProcessInstanceByKey(
"complexLeaveProcess",
String.valueOf(request.getId()),
variables
);
}
/**
* 获取用户的待办任务(包含任务详情)
*/
public List<TaskDetailDTO> getTaskDetailsForUser(String userId) {
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee(userId)
.orderByTaskCreateTime().desc()
.list();
return tasks.stream().map(task -> {
TaskDetailDTO detail = new TaskDetailDTO();
detail.setTaskId(task.getId());
detail.setTaskName(task.getName());
detail.setCreateTime(task.getCreateTime());
detail.setProcessInstanceId(task.getProcessInstanceId());
// 获取流程变量
Map<String, Object> variables = taskService.getVariables(task.getId());
detail.setVariables(variables);
// 获取业务信息
String businessKey = runtimeService.createProcessInstanceQuery()
.processInstanceId(task.getProcessInstanceId())
.singleResult()
.getBusinessKey();
detail.setBusinessKey(businessKey);
return detail;
}).collect(Collectors.toList());
}
/**
* 完成任务并传递变量
*/
public void completeTaskWithDecision(String taskId, String decision, String comment) {
Map<String, Object> variables = new HashMap<>();
variables.put("approvalResult", decision);
variables.put("approvalComment", comment);
variables.put("approvalTime", new Date());
if ("reject".equals(decision)) {
variables.put("finalStatus", "REJECTED");
}
taskService.complete(taskId, variables);
// 如果是最后一个审批任务且被批准,更新最终状态
if ("approve".equals(decision)) {
checkAndUpdateFinalStatus(taskId);
}
}
/**
* 检查并更新最终状态
*/
private void checkAndUpdateFinalStatus(String completedTaskId) {
Task completedTask = taskService.createTaskQuery().taskId(completedTaskId).singleResult();
String processInstanceId = completedTask.getProcessInstanceId();
// 检查是否还有未完成的审批任务
long activeTaskCount = taskService.createTaskQuery()
.processInstanceId(processInstanceId)
.count();
if (activeTaskCount == 0) {
// 所有审批完成,更新最终状态为已批准
runtimeService.setVariable(processInstanceId, "finalStatus", "APPROVED");
}
}
/**
* 查询流程历史记录
*/
public ProcessHistoryDTO getProcessHistory(String businessKey) {
ProcessHistoryDTO history = new ProcessHistoryDTO();
// 获取流程实例历史
HistoricProcessInstance processInstance = historyService
.createHistoricProcessInstanceQuery()
.processInstanceBusinessKey(businessKey)
.singleResult();
history.setProcessInstance(processInstance);
// 获取任务历史
List<HistoricTaskInstance> taskHistory = historyService
.createHistoricTaskInstanceQuery()
.processInstanceBusinessKey(businessKey)
.orderByHistoricTaskInstanceStartTime().asc()
.list();
history.setTaskHistory(taskHistory);
// 获取流程变量历史
List<HistoricVariableInstance> variableHistory = historyService
.createHistoricVariableInstanceQuery()
.processInstanceBusinessKey(businessKey)
.list();
history.setVariableHistory(variableHistory);
return history;
}
}
/**
* 请假请求DTO
*/
@Data
class ComplexLeaveRequest {
private Long id;
private String employeeId;
private String departmentId;
private Integer days;
private String reason;
private Date startDate;
private Date endDate;
}
/**
* 任务详情DTO
*/
@Data
class TaskDetailDTO {
private String taskId;
private String taskName;
private Date createTime;
private String processInstanceId;
private String businessKey;
private Map<String, Object> variables;
}
/**
* 流程历史DTO
*/
@Data
class ProcessHistoryDTO {
private HistoricProcessInstance processInstance;
private List<HistoricTaskInstance> taskHistory;
private List<HistoricVariableInstance> variableHistory;
}
八、总结
Activity工作流引擎将抽象的流程定义转化为可执行的代码,是企业应用开发中处理复杂业务流程的强大工具。通过 BPMN 2.0标准图 与 清晰的API服务,它极大地提升了流程类应用的开发效率和可维护性。
核心价值
- 可维护性高:流程逻辑与业务代码解耦。当业务流程发生变化时(例如,增加一个总监审批环节),通常只需要修改BPMN图并重新部署,无需修改Java代码。
- 可视化与可追溯性 :BPMN图使业务流程一目了然。
HistoryService可以让你轻松查看任何一个流程实例的完整执行路径。 - 提高开发效率:开发人员更专注于每个节点的具体业务实现,而复杂的流程流转、持久化、事务管理等都由引擎自动处理。
- 灵活性:支持动态调整流程、任务跳转、委托等高级功能,能够适应复杂的业务场景。
- 强大的生态与集成:与Spring家族无缝集成,并提供了REST API,便于前后端分离架构。
适用场景
- 审批流系统:请假、报销、采购等审批流程
- 订单处理:电商订单的状态流转和处理
- 工单系统:客户服务请求的分配和处理
- 合同管理:合同起草、审批、签订的全生命周期管理
从简单的线性审批到包含并行网关、子流程、边界事件 的复杂场景,Activity都能游刃有余。通过与 Spring Boot 的集成 ,可以快速搭建企业级应用。而流程变量 和监听器等高级功能,则为实现精细化的业务控制提供了可能。
希望本篇完整的指南能帮助你系统性地掌握Activity,并将其成功应用到你的项目中,最终实现业务流程的自动化与智能化管理。
扩展学习方向:
- 探索Activity Modeler进行可视化流程设计
- 学习Activiti的REST API,构建前后端分离的工作流平台
- 研究历史数据的高效查询与清理策略
- 了解流程实例的迁移和版本管理策略
- 探索与规则引擎(Drools)的集成实现动态路由