梳理流程引擎 Flowable 四大常见工作
1. ReceiveTask
1.1 应用场景
接受任务(Receive Task),接触过 Flowable 的小伙伴应该是见过或者据说过,它的图标如下图:
ReceiveTask 能够算是 Flowable 中最简略的一种工作,当该工作达到的时候,它不做任何逻辑,而是被动地期待用户 Trigger。
ReceiveTask 往往实用于一些不明确的阻塞,例如:一个简单的计算须要期待很多条件,这些条件是须要人为来判断是否能够执行,而不是间接执行,这个时候,工作人员如果判断能够持续了,那么就 Trigger 一下使流程持续向下执行。
基于以上介绍,ReceiveTask 还有一个中文名字叫做期待工作,也就是说,流程走到 ReceiveTask 这个节点的时候,就卡住了,须要用户手动点一下,流程才会持续向下走。
**1.2 实际
1.2.1 绘制流程图 **
咱们绘制一个简略的流程图来看下 ReceiveTask 到底是啥样子,流程图如下:
ReceiveTask 图标上有一个信封。
小伙伴们绘制的时候,首先抉择用户工作:
而后点击设置按钮,将用户工作切换为 ReceiveTask 即可:
绘制实现后,咱们下载这个流程图对应的 XML 文件。
来看看,带 ReceiveTask 的流程图是上面这样的:
<process id="receiveTask_demo" name="接管工作测试流程" isExecutable="true">
<documentation> 接管工作测试流程 </documentation>
<startEvent id="startEvent" flowable:formFieldValidation="true"></startEvent>
<sequenceFlow id="sid-9E7B327E-EFC8-4D29-8C6F-157D5E1B7A4E" sourceRef="startEvent" targetRef="todaySales"></sequenceFlow>
<receiveTask id="todaySales" name="统计今日销售额"></receiveTask>
<receiveTask id="sendMsg" name="发送今日销售业绩给老板"></receiveTask>
<endEvent id="endEvent"></endEvent>
<sequenceFlow id="s2" sourceRef="todaySales" targetRef="sendMsg"></sequenceFlow>
<sequenceFlow id="s3" sourceRef="sendMsg" targetRef="endEvent"></sequenceFlow>
</process>
复制代码
1.2.2 部署
这个松哥在之前的文章中曾经重复介绍过屡次了,这里就不再赘述了,大家参考咱们之前的文章部署并启动下面这个流程。
1.2.3 剖析
当流程启动之后,依照咱们后面文章的剖析,咱们先去数据库中 ACT_RU_TASK 表进行查看,发现该表空洞无物。也就是 ReceiveTask 并不会被记录在 ACT_RU_TASK 表中,他们只是单纯的被记录在 ACT_RU_EXECUTION 表中,因为在该表中,咱们能够查看 ReceiveTask 的记录。
对于 ReceiveTask 的触发形式也很简略,如下:
@Test
void test10() {List<Execution> list = runtimeService.createExecutionQuery().activityId("todaySales").list();
for (Execution execution : list) {runtimeService.trigger(execution.getId());
}
}
复制代码
因为 ReceiveTask 的触发须要传入的参数是执行实例 ID 而不是流程实例 ID,所以咱们要查问进去以后待触发的执行实例 ID。具体的查问形式就是依据 ReceiveTask 的节点名称去查问。
查问到执行实例 ID 之后,调用 trigger 办法实现触发,使得流程持续向下走。
好啦,当初流程进入到发送今日销售业绩给老板这个环节了,老办法持续查问并执行:
@Test
void test10() {List<Execution> list = runtimeService.createExecutionQuery().activityId("sendMsg").list();
for (Execution execution : list) {runtimeService.trigger(execution.getId());
}
}
复制代码
这个执行完层后,这个流程就完结了。当初咱们去查看 ACT_RU_ACTINST 表曾经空了,查看 ACT_RU_EXECUTION 表也空了。
2. UserTask
UserTask 看名字就晓得,须要人工干预,而人工解决的形式有很多种,咱们能够设置节点是由哪个用户解决,也能够设置是由哪个用户组来解决(相当于是由哪个角色来解决)。
当初,假如我有如下一个简略的流程图:
那么我该如何设置这个用户节点的解决人呢?
2.1 指定具体用户
第一种形式,是咱们在绘制流程图的时候,能够选中这个节点,而后间接设置流程的解决人,像上面这样:
而后在关上的窗口中抉择固定值,设置具体调配的用户是 javaboy,如下图:
好了,当初这个节点就固定的由一个名为 javaboy 的用户去解决了。
对应的 XML 文件如下:
<process id="demo01" name="demo01" isExecutable="true">
<documentation>demo01</documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" flowable:assignee="javaboy" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-71FB3A81-F753-419D-9A0A-2FC6E5361CED" sourceRef="startEvent1" targetRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3"></sequenceFlow>
<endEvent id="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></endEvent>
<sequenceFlow id="sid-DEBE03CD-F247-4EF3-BB67-ABBA94739B0A" sourceRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" targetRef="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></sequenceFlow>
</process>
复制代码
在下面这段 XML 中,小伙伴们看到 UserTask 节点中有一个 flowable:assignee=”javaboy”,这句话就是设置这个 UserTask 的解决人。
接下来,咱们部署并启动这个流程(具体的部署启动形式能够参考本系列之前的文章),启动之后,咱们能够在数据库的 ACT_RU_TASK 表中看到,这个 UserTask 的解决人是 javaboy,如下图:
当初咱们能够通过 Java 代码去查问 javaboy 须要解决的 UserTask 了,如下:
@Autowired
TaskService taskService;
@Test
void test11() {List<Task> list = taskService.createTaskQuery().taskAssignee("javaboy").list();
for (Task task : list) {logger.info("id:{},name:{}",task.getId(),task.getName());
}
}
复制代码
这个查问,实质上其实就是去 ACT_RU_TASK 表中查问的,咱们来看看执行的 SQL:
查问到这个工作之后,javaboy 有两种抉择:
将这个工作指定给另外一个人,例如 zhangsan。
本人解决。
2.1.1 从新指定工作解决人
假如 javaboy 查问到本人的工作之后,想把这个工作交给 zhangsan 去解决,形式如下:
@Autowired
TaskService taskService;
@Test
void test11() {List<Task> list = taskService.createTaskQuery().taskAssignee("javaboy").list();
for (Task task : list) {taskService.setAssignee(task.getId(),"zhangsan");
}
}
复制代码
这行代码执行实现后,咱们看数据库中的 ACT_RU_TASK 表,还是方才那条记录,然而解决人变了,变成了 zhangsan:
小伙伴们看到,版本号从 1 变为 2 了,阐明这条记录被更新过了,解决人则从 javaboy 变为了 zhangsan。
最初咱们再来看下这个操作所执行的 SQL,来验证一下咱们后面的论断:
小伙伴们留神看这里执行的 SQL,以及对应的参数,阐明咱们下面的剖析是没有问题的。
2.1.2 本人解决
如果 javaboy 想本人解决这个工作也是能够的,形式如下:
@Autowired
TaskService taskService;
@Test
void test11() {List<Task> list = taskService.createTaskQuery().taskAssignee("javaboy").list();
for (Task task : list) {taskService.complete(task.getId());
}
}
复制代码
解决实现后,ACT_RU_TASK 表中的记录也会被主动删除掉(执行过的 UserTask 会被主动删除)。
这种形式是指定了具体的用户,很显然这种硬编码的形式应用起来很不不便,咱们须要的是可能动静指定工作解决人的形式。
2.2. 通过变量设置
如果想动静指定 UserTask 的解决人,则能够通过变量来实现,具体形式如下:
在绘制流程图的时候,还是指定流程的具体解决人,然而在指定的时候,应用变量代替,如下图:
这里的 #{manager} 示意这个 UserTask 由一个名为 manager 的变量来指定,此时的 XML 文件则是上面这样:
<process id="demo01" name="demo01" isExecutable="true">
<documentation>demo01</documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" flowable:assignee="#{manager}" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-71FB3A81-F753-419D-9A0A-2FC6E5361CED" sourceRef="startEvent1" targetRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3"></sequenceFlow>
<endEvent id="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></endEvent>
<sequenceFlow id="sid-DEBE03CD-F247-4EF3-BB67-ABBA94739B0A" sourceRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" targetRef="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></sequenceFlow>
</process>
复制代码
小伙伴们看到,UserTask 节点中的 flowable:assignee=”#{manager}” 就示意这个 UserTask 的解决人由 manager 变量指定。
对于这样的流程,咱们在上一个节点处就须要指定下一个节点的解决人,对于以后案例来说,当然是要在流程启动的时候,指定这个 UserTask 的解决人,形式如下:
@Test
void test01() {Map<String, Object> variables = new HashMap<>();
variables.put("manager", "javaboy");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("demo01",variables);
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
复制代码
当流程启动胜利之后,大家去查看 ACT_RU_TASK 表,就能够看到,有一个待处理的 UserTask,解决人是 javaboy,如下图:
能看到这条记录,就阐明这个 UserTask 的解决人咱们曾经设置胜利了。
接下来具体的解决逻辑,则参考 1.1 和 1.2 大节。
2.3. 通过监听器设置
当然,咱们也能够通过监听器来设置工作的解决人。具体形式如下:
首先咱们在绘制流程图的时候,不须要给 UserTask 调配用户,如下图:
而后咱们为这个 UserTask 设置一个工作监听器,步骤如下:
首先点击 + 号,而后抉择 CREATE 事件,最初再给出事件对应的实体类,如下:
当然这个实体类是咱们我的项目中实在存在的一个类,如下:
public class MyTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {delegateTask.setAssignee("javaboy");
}
}
复制代码
当这个 UserTask 创立的时候,就会触发这个监听器,为该 UserTask 设置解决人。
咱们来看看这个流程图对应的 XML 文件是什么样子的:
<process id="demo01" name="demo01" isExecutable="true">
<documentation>demo01</documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" flowable:formFieldValidation="true">
<extensionElements>
<flowable:taskListener event="create" class="org.javaboy.flowableidm.MyTaskListener"></flowable:taskListener>
</extensionElements>
</userTask>
<sequenceFlow id="sid-71FB3A81-F753-419D-9A0A-2FC6E5361CED" sourceRef="startEvent1" targetRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3"></sequenceFlow>
<endEvent id="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></endEvent>
<sequenceFlow id="sid-DEBE03CD-F247-4EF3-BB67-ABBA94739B0A" sourceRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" targetRef="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></sequenceFlow>
</process>
复制代码
小伙伴们看到,event=”create” class=”org.javaboy.flowableidm.MyTaskListener” 就是咱们设置的内容了。
当初咱们部署并启动这个流程,当咱们流程启动后,就能够在 ACT_RU_TASK 表中看到一条 javaboy 待处理的工作了。
2.4. 其余状况
最初再来说说一种非凡状况,就是这个 UserTask 由工作的发起人解决,工作是谁发动的,谁来解决人这个 UserTask。
这个首先须要在流程启动事件上设置工作的发起人变量名,如下,流程的启动节点,而后设置工作的发起人:
接下来,在给 UserTask 设置解决人的时候,设置解决人和工作的发起人的变量是同一个,如下图:
好啦,这就能够了。来看看对应的 XML 文件:
<process id="demo01" name="demo01" isExecutable="true">
<documentation>demo01</documentation>
<startEvent id="startEvent1" flowable:initiator="INITATOR" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" flowable:assignee="#{INITATOR}" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-71FB3A81-F753-419D-9A0A-2FC6E5361CED" sourceRef="startEvent1" targetRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3"></sequenceFlow>
<endEvent id="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></endEvent>
<sequenceFlow id="sid-DEBE03CD-F247-4EF3-BB67-ABBA94739B0A" sourceRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" targetRef="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></sequenceFlow>
</process>
复制代码
startEvent 中有一个 flowable:initiator="INITATOR" 示意设置流程发起人的变量为 INITATOR。后续在 UserTask 中应用该变量即可。将这个流程部署胜利之后,依照如下形式启动流程:@Test
void test01() {Authentication.setAuthenticatedUserId("javaboy");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("demo01");
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
复制代码
Authentication.setAuthenticatedUserId(“javaboy”); 示意设置流程的发起人为 javaboy。
后面都是针对单个工作解决人,有的时候,一个工作节点会存在多个候选人,例如 zhangsan 提交一个工作,这个工作即能够 lisi 解决,又能够 wangwu 解决,那么针对这种多个工作候选人的状况,咱们该如何解决?
2.5 流程图指定多个候选人
首先咱们还是应用之前旧的流程图,然而在为 UserTask 设置调配用户的时候,咱们设置多个用户,如下图:
设置实现后,咱们下载这个流程文件,来看下对应的 XML 文件,内容如下:
<process id="demo01" name="demo01" isExecutable="true">
<documentation>demo01</documentation>
<startEvent id="startEvent1" flowable:initiator="INITATOR" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" flowable:candidateUsers="javaboy,zhangsan,lisi" flowable:formFieldValidation="true"></userTask>
<sequenceFlow id="sid-71FB3A81-F753-419D-9A0A-2FC6E5361CED" sourceRef="startEvent1" targetRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3"></sequenceFlow>
<endEvent id="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></endEvent>
<sequenceFlow id="sid-DEBE03CD-F247-4EF3-BB67-ABBA94739B0A" sourceRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" targetRef="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></sequenceFlow>
</process>
复制代码
小伙伴们看到,UserTask 中的 flowable:candidateUsers=”javaboy,zhangsan,lisi” 就示意这个 UserTask 由 javaboy、zhangsan 和 lisi 三个用户解决,用户名之间用 , 隔开。
2.6 查问工作解决人
接下来咱们部署并启动下面这个流程,具体如何部署如何启动,这个在之前的文章中松哥曾经和大家聊过了,这里不再赘述。
当流程启动胜利之后,当初咱们很容易想到像之前文章那样,去查问 javaboy 须要解决的 UserTask,如下:
List<Task> list = taskService.createTaskQuery().taskAssignee("javaboy").list();
for (Task task : list) {logger.info("id:{};name:{};taskDefinitionKey:{}", task.getId(), task.getName(), task.getTaskDefinitionKey());
}
复制代码
然而咱们却发现这个 SQL 执行实现后,查问不到任何数据!为什么呢?咱们来剖析下。
通过后面几篇文章的介绍,当初小伙伴们都晓得了,下面这个办法最终查问的是数据库中的 ACT_RU_TASK 表,查问的 SQL 如下:
那咱们就去查看 ACT_RU_TASK 表以及它的 ASSIGNEE_ 字段,后果如下:
咱们发现 ACT_RU_TASK 表中记录的 ASSIGNEE_ 字段值为 null!
为 null 这个其实也好了解,毕竟这个 UserTask 有多集体能够解决,然而只有一个字段,没法贮存,必定有其余存储形式。
好啦,不和大家卖关子了,这种有多个候选人的工作,咱们应该依照如下形式来查问:
@Test
void test12() {List<Task> list = taskService.createTaskQuery().taskCandidateUser("javaboy").list();
for (Task task : list) {logger.info("id:{};name:{};taskDefinitionKey:{}", task.getId(), task.getName(), task.getTaskDefinitionKey());
}
}
复制代码
小伙伴们看到,这里应该调用 taskCandidateUser 办法进行解决。那么这个办法查问的是哪张表呢?咱们来看下下面办法最终执行的 SQL,如下:
: ==> Preparing: SELECT RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ?) ) order by RES.ID_ asc
: ==> Parameters: javaboy(String)
: <== Total: 1
复制代码
小伙伴们看到,这里的查问波及到两张表,别离是 ACT_RU_TASK 和 ACT_RU_IDENTITYLINK,两张表联结查问查出来的,那咱们来看看 ACT_RU_IDENTITYLINK 表的内容:
小伙伴们看到,TYPE_ 为 candidate 的就示意这个 Task 的候选人,id 为 c5693038-3f42-11ed-b9e2-acde48001122 的 Task 一共有三个候选人,两张表联结查问,才能够查到这个 UserTask 该由谁来解决。
另外一种常见的需要就是,曾经晓得了要解决的流程实例了,然而不晓得应该由谁来解决,此时通过查问 ACT_RU_IDENTITYLINK 表就能够确定一个流程实例都有哪些参与者,如下:
@Test
void test13() {List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();
for (ProcessInstance pi : list) {List<IdentityLink> identityLinksForProcessInstance = runtimeService.getIdentityLinksForProcessInstance(pi.getId());
for (IdentityLink identityLink : identityLinksForProcessInstance) {logger.info("ProcessInstanceId:{},UserId:{}",identityLink.getProcessInstanceId(),identityLink.getUserId());
}
}
}
复制代码
咱们来看看下面这个执行的 SQL,如下:
能够看到,其实就是通过查问 ACT_RU_IDENTITYLINK 表获取咱们想要的数据。
2.7 认领工作
对于这种有候选人的工作,咱们须要先认领,再解决。认领的实质,其实就是给 ACT_RU_TASK 表中,这个 UserTask 记录的 ASSIGNEE_ 字段设置上值。
认领工作的形式如下:
@Test
void test12() {List<Task> list = taskService.createTaskQuery().taskCandidateUser("javaboy").list();
for (Task task : list) {taskService.claim(task.getId(),"javaboy");
}
}
复制代码
认领之后,咱们再来看 ACT_RU_TASK 表中的数据,如下:
能够看到,此时 ASSIGNEE_ 字段就有值了,同时 CLAIM_TIME 字段也记录了工作的认领工夫。
再来看看工作认领执行的 SQL,基本上和咱们所想的统一。
2.8 解决工作
认领后的工作该如何解决,这个就和咱们上篇文章中介绍的形式统一了,如下:
@Test
void test11() {List<Task> list = taskService.createTaskQuery().taskAssignee("javaboy").list();
for (Task task : list) {taskService.complete(task.getId());
}
}
复制代码
具体原理上篇文章中曾经介绍过了,这里就不再赘述了。
工作执行实现后,ACT_RU_IDENTITYLINK 表中的记录也会随之删除。
2.9 变量与监听器
后面这种形式设置的工作候选人咱们是在绘制流程图的时候间接硬编码的,这显然不是一个好方法。如果能通过变量来传递工作的候选人,就会不便很多。
2.9.1 候选人变量
咱们能够在绘制流程图的时候,用变量代替间接指定候选人,形式如下:
此时,生成的流程 XML 文件中,UserTask 节点的解决人也就变成了上面这个样子:
<process id="demo01" name="demo01" isExecutable="true">
<documentation>demo01</documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" flowable:candidateUsers="${userIds}" flowable:formFieldValidation="true"></userTask>
<sequenceFlow id="sid-71FB3A81-F753-419D-9A0A-2FC6E5361CED" sourceRef="startEvent1" targetRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3"></sequenceFlow>
<endEvent id="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></endEvent>
<sequenceFlow id="sid-DEBE03CD-F247-4EF3-BB67-ABBA94739B0A" sourceRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" targetRef="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></sequenceFlow>
</process>
复制代码
UserTask 节点中的 flowable:candidateUsers=”${userIds}” 就示意流程的解决人由 userIds 变量管制。
好了,接下来咱们来启动流程,留神,此时启动流程须要传递 userIds 变量,如下:
@Test
void test01() {Map<String, Object> userIds = new HashMap<>();
userIds.put("userIds", "javaboy,zhangsan,lisi");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("demo01",userIds);
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
复制代码
多个用户之间,用英文 , 隔开。
好了,流程启动胜利后,接下来的操作参考 3、4 大节,这里我就不再赘述了。
2.9.2 监听器
当然,咱们也能够通过监听器来为 UserTask 设置多个候选解决人用户,首先咱们创立一个监听器如下:
public class MyUserTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {delegateTask.addCandidateUser("javaboy");
delegateTask.addCandidateUser("zhangsan");
delegateTask.addCandidateUser("lisi");
}
}
复制代码
而后在绘制流程图的时候,删除掉 UserTask 调配的用户,而后从新为 UserTask 设置一个监听器:
而后设置一个在创立 UserTask 的时候触发的监听器:
而后咱们下载这个流程图对应的 XML 文件,如下:
<process id="demo01" name="demo01" isExecutable="true">
<documentation>demo01</documentation>
<startEvent id="startEvent1" flowable:initiator="INITATOR" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" flowable:formFieldValidation="true">
<extensionElements>
<flowable:taskListener event="create" class="org.javaboy.flowableidm.MyUserTaskListener"></flowable:taskListener>
</extensionElements>
</userTask>
<sequenceFlow id="sid-71FB3A81-F753-419D-9A0A-2FC6E5361CED" sourceRef="startEvent1" targetRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3"></sequenceFlow>
<endEvent id="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></endEvent>
<sequenceFlow id="sid-DEBE03CD-F247-4EF3-BB67-ABBA94739B0A" sourceRef="sid-5F901234-AFF1-480E-9D66-2D196B910BA3" targetRef="sid-D0B9E5BF-8C1A-4F8F-B2C2-F423F5DC556D"></sequenceFlow>
</process>
复制代码
能够看到,在 userTask 节点中,通过 extensionElements 指定了额定的监听器。
好啦,这个流程当初就能够间接启动了,启动时也不须要额定的变量。
流程启动胜利后,接下来的操作参考 3、4 大节,这里我就不再赘述了。
2.10 工作回退
当一个工作认领(Claim)之后,然而又不想解决,此时咱们能够将工作退回。形式如下:
@Test
void test16() {List<Task> list = taskService.createTaskQuery().taskAssignee("javaboy").list();
for (Task task : list) {taskService.setAssignee(task.getId(), null);
}
}
复制代码
其实思路很简答,就是从新为工作设置解决人,且解决人为 null,这就是将工作回退了,接下来其他人能够从新认领该工作了。
2.11 批改工作候选人
2.11.1 减少
工作候选人也不是变化无穷的,也能够动静批改,当一个流程启动之后,流程曾经走到某一个 Task 了,此时咱们想要批改该 Task 的候选人,也是能够的,形式如下:
@Test
void test17() {List<Task> list = taskService.createTaskQuery().taskCandidateUser("javaboy").list();
for (Task task : list) {taskService.addCandidateUser(task.getId(),"wangwu");
}
}
复制代码
增加实现后,查看 ACT_RU_IDENTITYLINK 表,咱们发现 wangwu 曾经增加进来了:
2.11.2 删除
如果想要删除一个候选人,形式如下:
@Test
void test18() {List<Task> list = taskService.createTaskQuery().taskCandidateUser("javaboy").list();
for (Task task : list) {taskService.deleteCandidateUser(task.getId(), "wangwu");
}
}
复制代码
删除胜利之后,ACT_RU_IDENTITYLINK 表中对应的数据也就被革除掉了。
2.12 查问历史数据
如果一个流程执行完结了,咱们还想查问这个流程已经波及到的参与者,能够通过如下形式查问:
@Test
void test14() {List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().list();
for (HistoricProcessInstance historicProcessInstance : list) {List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForProcessInstance(historicProcessInstance.getId());
for (HistoricIdentityLink link : links) {logger.info("userId:{},taskId:{},type:{},processInstanceId:{}",link.getUserId(),link.getTaskId(),link.getType(),link.getProcessInstanceId());
}
}
}
复制代码
这里最终其实就是去 ACT_HI_IDENTITYLINK 表中查问,对应的 SQL 如下:
下面这是查问一个流程的参加人,当然咱们也能够查问一个 Task 的候选人与解决人,如下:
@Test
void test15() {List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().list();
for (HistoricTaskInstance historicTaskInstance : list) {List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForTask(historicTaskInstance.getId());
for (HistoricIdentityLink link : links) {logger.info("userId:{},taskId:{},type:{},processInstanceId:{}", link.getUserId(), link.getTaskId(), link.getType(), link.getProcessInstanceId());
}
}
}
复制代码
查问对应的 SQL 如下:
和咱们所想的基本一致。
后面松哥和大家分享的都是给 UserTask 设置解决人或者是候选用户,不过小伙伴们也晓得,在咱们为 UserTask 设置解决人的时候,除了设置单个的解决人,也能够设置 Group,就是某一个用户组内的所有用户都能够解决该 Task。
在 Flowable 中应用 Group 去归类某一类用户,然而这个实际上相似于咱们在本人零碎中平时所用的角色 Role。也就是说,咱们能够依照角色去给每一个 UserTask 设置解决人。
接下来松哥就来和小伙伴们聊一聊这里的一些细节。
2.13 用户与用户组
首先咱们先来看下用户组的一些基本操作。
2.13.1 增加组
组的属性相对来说少一些,增加形式和 user 比拟像:
@Test
void test09() {GroupEntityImpl g = new GroupEntityImpl();
g.setName("组长");
g.setId("leader");
g.setRevision(0);
identityService.saveGroup(g);
}
复制代码
增加之后,组的信息保留在 ACT_ID_GROUP 表中,如下图:
组创立好之后,接下来还要给组增加用户,增加形式如下:
identityService.createMembership("zhangsan", "leader");
identityService.createMembership("lisi", "leader");
复制代码
这就是设置 zhangsan 和 lisi 是组长(留神用户和组的关联关系表中有外键,所以须要确保两个参数都是实在存在的)。
增加了关联关系之后,咱们再去查看 ACT_ID_MEMBERSHIP 表,如下:
掉用如下办法能够删除关联关系:
identityService.deleteMembership("zhangsan","leader");
复制代码
2.13.2 批改组
如下,将 id 为 leader 的组名更新为主管,如下:
Group g = identityService.createGroupQuery().groupId("leader").singleResult();
g.setName("主管");
identityService.saveGroup(g);
复制代码
2.13.3 删除组
删除组形式如下:identityService.deleteGroup("leader");
复制代码
删除组的同时,也会删除掉组和用户之间的关联关系,不过不必放心用户被删除。
2.13.4 查问组
能够依据 id 或者 name 或者组员信息等去查问组:
// 依据 id 查问组信息
Group g1 = identityService.createGroupQuery().groupId("leader").singleResult();
System.out.println("g1.getName() =" + g1.getName());
// 依据 name 查问组信息
Group g2 = identityService.createGroupQuery().groupName("组长").singleResult();
System.out.println("g2.getId() =" + g2.getId());
// 依据用户查问组信息(组里蕴含该用户)List<Group> list = identityService.createGroupQuery().groupMember("zhangsan").list();
for (Group group : list) {System.out.println("group.getName() =" + group.getName());
}
复制代码
2.14 设置候选组
在咱们绘制流程图的时候,咱们能够为 UserTask 设置一个候选组,形式如下:
从这个中央大家也能够看到,后抉择是能够给多个的。
好了,设置实现后,咱们下载流程图的 XML 文件,而后来看下这个中央不同凡响之处:
<process id="demo01" name="测试流程" isExecutable="true">
<documentation> 测试流程 </documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-F2F3C468-79B9-447B-943F-7CD18CE9BECF" flowable:candidateGroups="leader" flowable:formFieldValidation="true"></userTask>
<sequenceFlow id="sid-79C79920-2AD8-48FE-A59C-CC4D23C1895D" sourceRef="startEvent1" targetRef="sid-F2F3C468-79B9-447B-943F-7CD18CE9BECF"></sequenceFlow>
<endEvent id="sid-2236991E-3643-4590-9001-E22C256CA584"></endEvent>
<sequenceFlow id="sid-51105EB7-07F6-4190-9B2E-8F1F20A307D1" sourceRef="sid-F2F3C468-79B9-447B-943F-7CD18CE9BECF" targetRef="sid-2236991E-3643-4590-9001-E22C256CA584"></sequenceFlow>
</process>
复制代码
小伙伴们看到,flowable:candidateGroups=”leader” 就示意这个工作由一个候选用户组来解决,如果有多个候选的用户组,则不同用户组之间用 , 隔开。
当然,这是硬编码。如果想像候选用户一样,通过动静变量来传递用户组名称也是能够的,具体做法像上面这样:
这样,最终生成的 XML 文件则相似这样:flowable:candidateGroups=”${g1}”。
2.15 依据用户组查问工作
接下来,咱们部署并启动一个流程,具体的部署和启动形式松哥在之前的文章中都曾经和大家介绍过了,这里简略看下办法就行了:
@Test
void test01() {Map<String, Object> variables = new HashMap<>();
variables.put("g1", "leader");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("demo01",variables);
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
复制代码
这个就是流程启动的过程,留神启动的时候加了参数,用来形容下一个 UserTask 的解决组。
启动胜利之后,咱们能够在 ACT_RU_IDENTITYLINK 表中查看到用户组和 UserTask 之间的关系:
接下来咱们能够依照查问候选人工作的形式,查问 zhangsan 须要实现的工作,如下:
@Test
void test19() {List<Task> list = taskService.createTaskQuery().taskCandidateUser("zhangsan").list();
for (Task task : list) {logger.info("name:{},createTime:{}", task.getName(), task.getCreateTime());
}
}
复制代码
这个查问的外部实现,咱们能够拆分为两步:
查问进去 zhangsan 是属于哪个 group,这个查问执行的 SQL 如下:
SELECT RES.* from ACT_ID_GROUP RES WHERE exists(select 1 from ACT_ID_MEMBERSHIP M where M.GROUP_ID_ = RES.ID_ and M.USER_ID_ = ?) order by RES.ID_ asc
复制代码
这个查问中有一个参数,参数的值就是 zhangsan,下面这个 SQL 能够查问进去 zhangsan 这个用户属于 leader 这个分组,在接下来的查问中,会 zhangsan 和 leader 两个参数都会用到。
查问 zhangsan 或者 leader 的工作,执行 SQL 如下:
SELECT RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ? or ( LINK.GROUP_ID_ IN ( ?) ) ) ) order by RES.ID_ asc
复制代码
能够看到,这个查问里,有两个参数了,两个参数的值别离是 zhangsan 和 leader。
也就是说,尽管咱们这里代码写的是依照 zhangsan 去查问,实际上查问的是 zhangsan 所属的用户组的 Task(这个逻辑也好了解,因为 zhangsan 所属的用户组的 Task 实际上也就是 zhangsan 的 Task)。
当然,咱们也能够间接依照 group 去查问,如下:
@Test
void test20() {List<Task> list = taskService.createTaskQuery().taskCandidateGroup("leader").list();
for (Task task : list) {logger.info("name:{},createTime:{}", task.getName(), task.getCreateTime());
}
}
复制代码
这个查问原理跟下面的差不多,不过省事的是,这里一条 SQL 就搞定了(不须要依据用户名查问用户所属的分组了),如下:
SELECT RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( ( LINK.GROUP_ID_ IN ( ?) ) ) ) order by RES.ID_ asc
复制代码
好啦,当这些工作查问进去后,接下来该如何执行,就和后面介绍的内容一样了,我这里就不再赘述了。
- ServiceTask
ServiceTask 从名字上看就是服务工作,它的图标个别是像上面这样:
ServiceTask 个别由零碎主动实现,当流程走到这一步的时候,不会主动停下来,而是会去执行咱们提前在 ServiceTask 中配置好的办法。
3.1 实际
咱们通过一个简略的例子来看一下 ServiceTask 要怎么玩。
假如我有如下一个简略的流程图:
两头这个就是一个 ServiceTask。
当流程执行到 ServiceTask 的时候,具体要做哪些事件?有三种不同的形式来设置这里的工作,咱们别离来看。
3.1.1 监听类
首先咱们能够设置一个监听类,这个监听类有一个硬性规定就是须要实现 JavaDelegate 接口,像上面这样:
public class MyServiceTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {System.out.println("========MyServiceTask==========");
}
}
复制代码
在这个监听类中咱们能够实现一些操作,通过这个 execution 也能够获取到在流程节点之间传输的变量。
这个类定义好之后,接下来咱们在流程定义的时候,配置这个类的全门路即可,如下图:
这个配置对应的 XML 内容如下:
<process id="demo01" name="测试流程" isExecutable="true">
<documentation> 测试流程 </documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<sequenceFlow id="sid-33A78082-C2FD-48BE-8B87-99FB20F0B331" sourceRef="startEvent1" targetRef="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8"></sequenceFlow>
<serviceTask id="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8" flowable:class="org.javaboy.flowableidm.MyServiceTask"></serviceTask>
<endEvent id="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></endEvent>
<sequenceFlow id="sid-0698809E-0A6C-4B92-A167-AE96A8CB75F2" sourceRef="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8" targetRef="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></sequenceFlow>
</process>
复制代码
小伙伴们看到,在 ServiceTask 标签中的 flowable:class=”org.javaboy.flowableidm.MyServiceTask” 就示意 ServiceTask 执行的服务类。
配置实现后,咱们能够部署并启动这个流程,因为这个流程除了开始和完结,就这一个节点,所以流程一启动就主动完结了。不过在这个过程中,咱们能够看到控制台打印进去了日志,阐明这个 ServiceTask 的确是执行了。
3.1.2 委托表达式
咱们也能够配置委托表达式。
委托表达式是指将一个实现了 JavaDelegate 接口的类注册到 Spring 容器中,而后咱们在流程节点的配置中不必写残缺的类名了,只须要写 Spring 容器中的 Bean 名称即可。
像上面这样:
@Component
public class MyServiceTask implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {System.out.println("========MyServiceTask==========");
}
}
复制代码
这个类注册到 Spring 容器中的默认名称是类名首字母小写,即 myServiceTask。
当初咱们在流程图中,能够依照如下形式进行配置:
对应的 XML 文件如下:
<process id="demo01" name="测试流程" isExecutable="true">
<documentation> 测试流程 </documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<sequenceFlow id="sid-33A78082-C2FD-48BE-8B87-99FB20F0B331" sourceRef="startEvent1" targetRef="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8"></sequenceFlow>
<serviceTask id="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8" flowable:delegateExpression="${myServiceTask}"></serviceTask>
<endEvent id="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></endEvent>
<sequenceFlow id="sid-0698809E-0A6C-4B92-A167-AE96A8CB75F2" sourceRef="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8" targetRef="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></sequenceFlow>
</process>
复制代码
能够看到,flowable:delegateExpression=”${myServiceTask}” 就示意执行的一个表达式。
测试过程同 2.1 大节,我就不再赘述了。
最初总结一下,委托表达式,肯定是 JavaDelegate 接口的实现类,将这个实现类注册到 Spring 容器中,而后在应用的时候,依据 Bean 的名称从 Spring 容器中查找即可。
3.1.3 表达式
咱们也能够应用表达式。
表达式就是一个一般类的一般办法,将这个一般类注册到 Spring 容器中,而后表达式中还能够执行这个类中的办法,相似上面这样,任意定义一个 Java 类:
@Component
public class MyServiceTask2 {public void hello() {System.out.println("========MyServiceTask2==========");
}
}
复制代码
而后在流程图中依照如下形式进行配置:
表达式中有一部分内容暗藏了,残缺的表达式是 ${myServiceTask2.hello()}。
对应的 XML 文件如下:
<process id="demo01" name="测试流程" isExecutable="true">
<documentation> 测试流程 </documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<sequenceFlow id="sid-33A78082-C2FD-48BE-8B87-99FB20F0B331" sourceRef="startEvent1" targetRef="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8"></sequenceFlow>
<serviceTask id="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8" flowable:expression="${myServiceTask2.hello()}"></serviceTask>
<endEvent id="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></endEvent>
<sequenceFlow id="sid-0698809E-0A6C-4B92-A167-AE96A8CB75F2" sourceRef="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8" targetRef="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></sequenceFlow>
</process>
复制代码
能够看到,表达式的内容是 flowable:expression=”${myServiceTask2.hello()}。
测试形式同 2.1 大节,这里我不再赘述。
3.2 类中字段
可能有小伙伴留神到,咱们在绘制流程图的时候,还能够为类设置一个字段。
例如我想给 ServiceTask 的执行类设置一个 username 字段,如下:
设置实现后,对应的 XML 如下:
<process id="demo01" name="测试流程" isExecutable="true">
<documentation> 测试流程 </documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<sequenceFlow id="sid-33A78082-C2FD-48BE-8B87-99FB20F0B331" sourceRef="startEvent1" targetRef="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8"></sequenceFlow>
<serviceTask id="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8" flowable:delegateExpression="${myServiceTask}">
<extensionElements>
<flowable:field name="username">
<flowable:string><![CDATA[javaboy]]></flowable:string>
</flowable:field>
</extensionElements>
</serviceTask>
<endEvent id="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></endEvent>
<sequenceFlow id="sid-0698809E-0A6C-4B92-A167-AE96A8CB75F2" sourceRef="sid-6FA66E2A-F8E6-4F10-8FA2-6450408E17D8" targetRef="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></sequenceFlow>
</process>
复制代码
能够看到,这里通过 extensionElements 节点形容了额定的信息。
接下来,咱们就能够在 Java 类中拜访到这个变量了,如下:
@Component
public class MyServiceTask implements JavaDelegate {
Expression username;
@Override
public void execute(DelegateExecution execution) {System.out.println("username.getExpressionText() =" + username.getExpressionText());
System.out.println("username.getValue(execution) =" + username.getValue(execution));
System.out.println("========MyServiceTask==========");
}
}
复制代码
想要获取到 username 对应的值,下面这段代码中,松哥给大家演示了两种形式。
- 脚本工作
个人感觉脚本工作和咱们后面说的 ServiceTask 很像,都是流程走到这个节点的时候主动做一些事件,不同的是,在 ServiceTask 中,流程在这个节点中所做的事件是用 Java 代码写的,在脚本工作中,流程在这个节点中所做的事件则是用其余一些脚本语言如 JavaScript、Groovy、Juel 等写的。
脚本工作的图标如下图所示:
4.1 实际
写一个简略的例子咱们来一起看下。
4.1.1 JavaScript 脚本
咱们先来看用 JavaScript 写这个脚本。
假如我有如下流程图:
两头这个节点就是一个脚本工作。
选中该节点,咱们先配置脚本语言是 JavaScript,如下图:
这里也能够应用简写的 js。
而后再点击左边的脚本,配置脚本,如下图:
下面这里我写了两行 JavaScript 脚本:
第一行示意流程执行到这里的时候,须要做一个简略的加法运算,a 和 b 两个变量则须要流程传入进来。
第二行示意往流程中存储一个名为 sum 的变量,变量值就是后面计算的后果,其中 execution 是一个内置变量。这个就相似于咱们启动流程时候传入的变量一样。
在 ES6 中咱们罕用的 let 关键字这里并不反对,这个中央小伙伴们要留神。
配置实现之后,咱们下载这个脚本来看下对应的 XML 文件是什么样子:
<process id="demo01" name="测试流程" isExecutable="true">
<documentation> 测试流程 </documentation>
<startEvent id="startEvent1" flowable:formFieldValidation="true"></startEvent>
<sequenceFlow id="sid-33A78082-C2FD-48BE-8B87-99FB20F0B331" sourceRef="startEvent1" targetRef="sid-8D88DFF6-0F37-42FA-9F94-29FE30536094"></sequenceFlow>
<endEvent id="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></endEvent>
<sequenceFlow id="sid-0698809E-0A6C-4B92-A167-AE96A8CB75F2" sourceRef="sid-8D88DFF6-0F37-42FA-9F94-29FE30536094" targetRef="sid-A5F11956-15EA-4574-98D0-29A4E3DB5495"></sequenceFlow>
<scriptTask id="sid-8D88DFF6-0F37-42FA-9F94-29FE30536094" scriptFormat="JavaScript" flowable:autoStoreVariables="false">
<script><![CDATA[var sum=a+b;
execution.setVariable("sum",sum);]]></script>
</scriptTask>
</process>
复制代码
小伙伴们看到,scriptTask 中内嵌了一个 script 节点,里边就是咱们本人写的脚本内容。
好啦,接下来小伙伴们就能够部署并启动这个流程了,启动代码如下:
@Test
void test01() {Map<String, Object> variables = new HashMap<>();
variables.put("a", 99);
variables.put("b", 98);
ProcessInstance pi = runtimeService.startProcessInstanceByKey("demo01", variables);
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
复制代码
大家留神启动的时候传递 a 和 b 两个变量。这个流程启动之后,间接就执行完结了,因为流程达到 scriptTask 并不会进行。
不过咱们能够在 ACT_HI_VARINST 表中查看流程运行信息:
能够看到,相干的变量和变量值都保留着。
4.1.2 Groovy 脚本
看懂了 JavaScript 脚本,Groovy 就好懂了。不过 JavaScript 脚本预计大部分搞 Java 的小伙伴都懂,然而 Groovy 可能会比拟生疏,我简略介绍下:
Groovy 是 Apache 旗下的一门基于 JVM 平台的动静 / 麻利编程语言,在语言的设计上它吸纳了 Python、Ruby 和 Smalltalk 语言的优良个性,语法十分简练和柔美,开发效率也十分高(编程语言的开发效率和性能是互相矛盾的,越高级的编程语言性能越差,因为意味着更多底层的封装,不过开发效率会更高,需联合应用场景做取舍)。并且,Groovy 能够与 Java 语言无缝对接,在写 Groovy 的时候如果遗记了语法能够间接按 Java 的语法持续写,也能够在 Java 中调用 Groovy 脚本,都能够很好的工作,这无效的升高了 Java 开发者学习 Groovy 的老本。Groovy 也并不会代替 Java,而是相辅相成、互补的关系,具体应用哪门语言这取决于要解决的问题和应用的场景。
如果咱们想要在流程中应用 Groovy 脚本,那么首先设置脚本格局为 Groovy:
而后设置脚本内容如下:
这段脚本示意流程执行到这个节点的时候输入一个 “hello groovy”(如果你相熟 Groovy 脚本的话,就晓得这段脚本其实也能够间接写 Java 代码,也能执行)。
另外说一句,应用 Groovy 脚本,千万别忘了加 Groovy 依赖,如下:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>3.0.13</version>
</dependency>
复制代码
4.1.3 Juel 脚本
Juel 是 Java Unified Expression Language 的简称,它具备高性能,插件式缓存,小体积,反对办法调用和多参数调用,可插拔等多种个性,它是 JSP2.1 规范 (JSR-245) 中定义的一部分。只管 EL 表达式是随同着 JSP 而生,但当初曾经能够在非 JS P 利用中应用,相干的 API 放在 javax.el 包外面。
其实像咱们之前写的 ${xxx} 这种表达式,其实就是 Juel 了。
来一个简略的例子看下。假如咱们想在流程中应用 juel,首先设置脚本格局为 juel:
而后就能够设置具体的脚本内容了,如下:
这段脚本就示意调用一个名为 myServiceTask2 的 Bean 中的 hello 办法。