文章目录
- 一、Expression 是什么?
- 二、Expression 的类型
- 三、Expression 如何被注入?
- 四、在 BPMN 文件中配置
- 五、Expression 的常用方法
- 六、支持多种表达式类型(完整示例)
- 七、抄送任务监听器中的使用
- 八、完整的改进监听器示例
一、Expression 是什么?
在Activiti或Flowable中,Expression是一个表达式对象,它可以用来存储流程定义中配置的表达式。这个表达式可以是固定值,也可以是动态的(比如使用UEL表达式)。在流程定义中,我们可以在监听器配置中设置表达式的值。
二、Expression 的类型
Flowable 支持多种表达式类型:
| 类型 | 示例 | 说明 |
|---|---|---|
| 固定值 | user1,user2,user3 | 直接指定的值 |
| 变量表达式 | ${userId} | 从流程变量中获取 |
| 方法表达式 | ${bean.method(userId)} | 调用 Spring Bean 的方法 |
| 组合表达式 | ${userService.getCcUsers(processId)} | 复杂表达式 |
三、Expression 如何被注入?
在你的代码中:
privateExpressionuserIds;这个字段会被 Flowable 引擎自动注入,注入的来源是流程定义文件(BPMN)中的配置。
四、在 BPMN 文件中配置
<!-- 方式1:直接指定用户ID(固定值) --><userTaskid="approveTask"name="审批任务"flowable:assignee="zhangsan"><extensionElements><flowable:taskListenerevent="create"class="com.ruoyi.flowable.listener.FlowCopyTaskListener"><flowable:fieldname="userIds"><flowable:string><![CDATA[1001,1002,1003]]></flowable:string></flowable:field></flowable:taskListener></extensionElements></userTask><!-- 方式2:使用变量(动态值) --><userTaskid="approveTask"name="审批任务"><extensionElements><flowable:taskListenerevent="create"class="com.ruoyi.flowable.listener.FlowCopyTaskListener"><flowable:fieldname="userIds"><flowable:expression><![CDATA[${ccUserIds}]]></flowable:expression></flowable:field></flowable:taskListener></extensionElements></userTask><!-- 方式3:调用Spring Bean方法 --><userTaskid="approveTask"name="审批任务"><extensionElements><flowable:taskListenerevent="create"class="com.ruoyi.flowable.listener.FlowCopyTaskListener"><flowable:fieldname="userIds"><flowable:expression><![CDATA[${userService.getCcUsers(execution.getVariable('deptId'))}]]></flowable:expression></flowable:field></flowable:taskListener></extensionElements></userTask>五、Expression 的常用方法
publicinterfaceExpression{// 获取表达式原文StringgetExpressionText();// 获取解析后的值ObjectgetValue(VariableScopevariableScope);// 设置值voidsetValue(Objectvalue,VariableScopevariableScope);// 检查是否为文字文本booleanisLiteralText();}六、支持多种表达式类型(完整示例)
@Slf4j@ComponentpublicclassFlowCopyTaskListenerimplementsTaskListener{privateExpressionuserIds;@Overridepublicvoidnotify(DelegateTaskdelegateTask){log.info("抄送任务监听器被触发, 任务ID: {}",delegateTask.getId());// 方法1:直接获取表达式文本(适用于固定值)StringexpressionText=userIds.getExpressionText();log.info("表达式原文: {}",expressionText);// 方法2:获取解析后的值(适用于变量表达式)Objectvalue=userIds.getValue(delegateTask.getExecution());log.info("解析后的值: {}",value);// 解析抄送人员IDList<String>userIdList=parseUserIds(value,expressionText,delegateTask);if(!CollectionUtils.isEmpty(userIdList)){processCopyTask(delegateTask,userIdList);}}/** * 解析用户ID列表 */privateList<String>parseUserIds(Objectvalue,StringexpressionText,DelegateTaskdelegateTask){List<String>userIdList=newArrayList<>();// 情况1:如果值是List类型if(valueinstanceofList){List<?>list=(List<?>)value;for(Objectitem:list){if(item!=null){userIdList.add(item.toString());}}}// 情况2:如果值是字符串(逗号分隔)elseif(valueinstanceofString){StringstrValue=(String)value;if(StringUtils.isNotBlank(strValue)){userIdList=Arrays.asList(strValue.split(","));}}// 情况3:直接使用表达式文本(固定值)elseif(StringUtils.isNotBlank(expressionText)){// 检查是否为变量表达式(以${开头}结尾)if(expressionText.startsWith("${")&&expressionText.endsWith("}")){// 尝试从流程变量中获取StringvariableName=expressionText.substring(2,expressionText.length()-1);ObjectvariableValue=delegateTask.getExecution().getVariable(variableName);if(variableValueinstanceofString){userIdList=Arrays.asList(((String)variableValue).split(","));}}else{// 直接按逗号分隔userIdList=Arrays.asList(expressionText.split(","));}}// 去除空白字符returnuserIdList.stream().map(String::trim).filter(StringUtils::isNotBlank).collect(Collectors.toList());}/** * 处理抄送任务 */privatevoidprocessCopyTask(DelegateTaskdelegateTask,List<String>userIdList){// ... 原有处理逻辑RepositoryServicerepositoryService=SpringUtils.getBean(RepositoryService.class);ISysCopyServicesysCopyService=SpringUtils.getBean(ISysCopyService.class);// ... 其他代码}}七、抄送任务监听器中的使用
在抄送任务监听器中,userIds是一个Expression类型的属性,它会在流程引擎运行到该任务节点时,根据流程定义中配置的表达式进行解析。
在流程定义XML中,配置监听器如下(flowable为例):
<userTaskid="myTask"name="My Task"><extensionElements><flowable:taskListenerevent="create"class="com.ruoyi.flowable.listener.FlowCopyTaskListener"><flowable:fieldname="userIds"><flowable:string><![CDATA[${抄送人员表达式}]]></flowable:string></flowable:field></flowable:taskListener></extensionElements></userTask>其中,${抄送人员表达式}可以是一个变量,也可以是多个用户ID的拼接字符串(如"1,2,3"),或者是一个返回用户ID列表的表达式。
在监听器类中,我们使用private Expression userIds;来接收这个表达式。当监听器被触发时,userIds已经被流程引擎注入,我们可以通过userIds.getExpressionText()来获取表达式字符串,或者通过userIds.getValue(delegateTask)来获取表达式的值(如果表达式是变量,则会解析为变量值)。
但是,注意:在上面的代码中,监听器直接使用了userIds.getExpressionText(),这意味着它获取的是表达式原文,而不是解析后的值。如果表达式是变量,比如c c U s e r I d s ,那么这里得到的就是 " {ccUserIds},那么这里得到的就是"ccUserIds,那么这里得到的就是"{ccUserIds}"字符串,而不是变量值。因此,这种方式只适用于表达式是固定字符串的情况(比如直接写死用户ID列表)。
如果要支持表达式动态解析,应该使用userIds.getValue(delegateTask)来获取解析后的值。例如,如果我们在流程变量中设置了ccUserIds为"1,2,3",那么userIds.getValue(delegateTask)将返回一个字符串"1,2,3"。然后我们可以按照逗号分割得到用户ID列表。
注意:userIds.getValue(delegateTask)返回的类型是Object,我们需要根据实际情况进行类型转换。如果表达式是多个用户ID的列表,也可以配置为集合类型,但通常我们使用逗号分隔的字符串。
因此,修改后的代码可以这样写:
如果表达式是固定字符串(比如"1,2,3"),那么使用getExpressionText()即可。
如果表达式是变量,那么应该使用getValue(delegateTask)。
但是,为了同时支持两种情况,我们可以这样做:先尝试用getValue(delegateTask)获取值,如果返回值为空,则使用getExpressionText()。或者,我们可以在流程定义中明确配置是固定值还是变量。
然而,在大多数情况下,我们更希望支持动态变量。所以,我们可以这样修改:
@Slf4j@ComponentpublicclassFlowCopyTaskListenerimplementsTaskListener{/** * 抄送人员id集合 */privateExpressionuserIds;@Overridepublicvoidnotify(DelegateTaskdelegateTask){log.info("抄送任务监听器, 任务ID:{}",delegateTask.getId());// 获取抄送人员ID字符串StringuserIdsStr=null;if(userIds!=null){// 尝试获取表达式的值(如果是变量,则解析变量)Objectvalue=userIds.getValue(delegateTask);if(value!=null){userIdsStr=value.toString();}else{// 如果解析变量后为空,则使用表达式原文(可能是固定值)userIdsStr=userIds.getExpressionText();}}if(StringUtils.isNotBlank(userIdsStr)){// 从 Spring 容器中获取相关服务RepositoryServicerepositoryService=SpringUtils.getBean(RepositoryService.class);ISysCopyServicesysCopyService=SpringUtils.getBean(ISysCopyService.class);ISysUserServicesysUserService=SpringUtils.getBean(ISysUserService.class);// 获取流程定义信息ProcessDefinitionpd=repositoryService.createProcessDefinitionQuery().processDefinitionId(delegateTask.getProcessDefinitionId()).singleResult();// 获取任务发起人信息,注意:这里delegateTask.getAssignee()是当前任务的办理人,不一定是流程发起人// 如果需要流程发起人,可以通过流程实例的启动人获取// 例如:String startUserId = delegateTask.getProcessInstance().getStartUserId();// 这里根据业务需求调整Stringassignee=delegateTask.getAssignee();SysUserstartUser=null;if(assignee!=null){startUser=sysUserService.selectUserById(Long.parseLong(assignee));}// 构造 SysCopy 对象SysCopysysCopy=newSysCopy();sysCopy.setProcInsId(delegateTask.getProcessInstanceId());sysCopy.setDeployId(pd.getDeploymentId());sysCopy.setProcName(pd.getName());sysCopy.setTaskId(delegateTask.getId());sysCopy.setTaskName(delegateTask.getName());if(startUser!=null){sysCopy.setSendUserId(startUser.getUserId());sysCopy.setSendUserName(startUser.getNickName());}sysCopy.setStatus(0);sysCopy.setCreateTime(newDate());// 分割用户ID字符串,注意处理可能的分隔符(逗号、分号等)List<String>userIdList=Arrays.asList(userIdsStr.split("[,;]"));userIdList.forEach(userId->{// 去除空白userId=userId.trim();if(StringUtils.isNotBlank(userId)){sysCopy.setReceiveUserId(Long.parseLong(userId));// 插入抄送记录sysCopyService.insertSysCopy(sysCopy);}});}}}注意:上面的代码中,我修改了获取流程发起人的方式。因为delegateTask.getAssignee()是当前任务的办理人,而抄送可能是由当前办理人发起的,所以这样设置。但是,如果抄送是要发送给其他人,而发起人应该是流程的启动人,那么就需要通过delegateTask.getProcessInstance().getStartUserId()来获取流程启动人。
另外,增加了sysCopy.setCreateTime(new Date());,因为通常插入记录需要创建时间。
还有,分割用户ID时,我支持了逗号和分号两种分隔符,并去除了空白。
这样修改后,监听器就可以支持表达式动态获取抄送人员列表了。
在流程定义中,我们可以这样配置:
固定值:1,2,3 变量:${ccUserIds},其中ccUserIds是流程变量,值为"1,2,3"这样,监听器就可以灵活地获取抄送人员了。
八、完整的改进监听器示例
下面是一个灵活的监听器类的示例:
@Slf4j@ComponentpublicclassEnhancedFlowCopyTaskListenerimplementsTaskListener{@AutowiredprivateRepositoryServicerepositoryService;@AutowiredprivateISysCopyServicesysCopyService;@AutowiredprivateISysUserServicesysUserService;@AutowiredprivateApplicationContextapplicationContext;privateExpressionuserIds;privateExpressioncopyType;// 抄送类型:固定、变量、方法privateExpressioncopyRule;// 抄送规则@Overridepublicvoidnotify(DelegateTaskdelegateTask){try{// 1. 获取流程信息ProcessDefinitionpd=repositoryService.createProcessDefinitionQuery().processDefinitionId(delegateTask.getProcessDefinitionId()).singleResult();// 2. 解析抄送人员(支持多种方式)List<Long>userIds=resolveUserIds(delegateTask);// 3. 获取发起人(改进:从流程实例启动人获取)StringstartUserId=delegateTask.getProcessInstance().getStartUserId();SysUserstartUser=sysUserService.selectUserById(Long.parseLong(startUserId));// 4. 创建抄送记录createCopyRecords(delegateTask,pd,startUser,userIds);}catch(Exceptione){log.error("抄送任务处理失败",e);}}/** * 解析用户ID(支持多种表达式类型) */privateList<Long>resolveUserIds(DelegateTaskdelegateTask){// 获取表达式值Objectvalue=userIds.getValue(delegateTask.getExecution());if(value==null){returnCollections.emptyList();}// 根据类型处理if(valueinstanceofString){StringstrValue=(String)value;returnArrays.stream(strValue.split(",")).map(String::trim).filter(StringUtils::isNotBlank).map(Long::parseLong).collect(Collectors.toList());}elseif(valueinstanceofCollection){Collection<?>collection=(Collection<?>)value;returncollection.stream().filter(Objects::nonNull).map(obj->Long.parseLong(obj.toString())).collect(Collectors.toList());}returnCollections.emptyList();}}在具体的代码中,直接使用 userIds.getExpressionText() 只适用于固定字符串的场景。如果要支持更复杂的场景(如从变量获取),应该使用 userIds.getValue(execution) 方法。
“人的一生会经历很多痛苦,但回头想想,都是传奇”。