Java 服务任务
描述
java服务任务用来调用外部java类。
图形标记
服务任务显示为圆角矩形,左上角有一个齿轮小图标。
XML
脚本任务定义需要指定script 和scriptFormat。
有4钟方法来声明java调用逻辑:
- 实现JavaDelegate或ActivityBehavior
- 执行解析代理对象的表达式
- 调用一个方法表达式
- 调用一直值表达式
执行一个在流程执行中调用的类, 需要在'activiti:class'属性中设置全类名。
<serviceTask id="javaService"
name="My Java Service Task"
activiti:class="org.activiti.MyJavaDelegate" />
也可以使用表达式调用一个对象。对象必须遵循一些规则, 并使用activiti:class属性进行创建。
<serviceTask id="serviceTask" activiti:delegateExpression="${delegateExpressionBean}" />
这里,delegateExpressionBean是一个实现了JavaDelegate接口的bean, 它定义在实例的spring容器中。
要指定执行的UEL方法表达式, 需要使用activiti:expression。
<serviceTask id="javaService"
name="My Java Service Task"
activiti:expression="#{printer.printMessage()}" />
方法printMessage(无参数)会调用 名为printer对象的方法。
也可以为表达式中的方法传递参数。
<serviceTask id="javaService"
name="My Java Service Task"
activiti:expression="#{printer.printMessage(execution, myVar)}" />
这会调用名为printer对象上的方法printMessage。 第一个参数是DelegateExecution,在表达式环境中默认名称为execution。 第二个参数传递的是当前流程的名为myVar的变量。
要指定执行的UEL值表达式, 需要使用activiti:expression属性。
<serviceTask id="javaService"
name="My Java Service Task"
activiti:expression="#{split.ready}" />
ready属性的getter方法,getReady(无参数), 会作用于名为split的bean上。 这个对象会被解析为流程对象和 (如果合适)spring环境中的对象。
实现
要在流程执行中实现一个调用的类,这个类需要实现org.activiti.engine.delegate.JavaDelegate接口, 并在execute方法中提供对应的业务逻辑。 当流程执行到特定阶段,它会指定方法中定义好的业务逻辑, 并按照默认BPMN 2.0中的方式离开节点。
让我们创建一个java类的例子,它可以流程变量中字符串转换为大写。 这个类需要实现org.activiti.engine.delegate.JavaDelegate接口, 这要求我们实现execute(DelegateExecution)方法。 它包含的业务逻辑会被引擎调用。流程实例信息,如流程变量和其他信息, 可以通过 DelegateExecution 接口访问和操作。
public class ToUppercase implements JavaDelegate {
public void execute(DelegateExecution execution) throws Exception {
String var = (String) execution.getVariable("input");
var = var.toUpperCase();
execution.setVariable("input", var);
}
}
注意:serviceTask定义的class只会创建一个java类的实例。 所有流程实例都会共享相同的类实例,并调用execute(DelegateExecution)。 这意味着,类不能使用任何成员变量,必须是线程安全的,它必须能模拟在不同线程中执行。 这也影响着属性注入的处理方式。
流程定义中引用的类(比如,使用activiti:class)不会 在部署时实例化。只有当流程第一次执行到使用类的时候, 类的实例才会被创建。如果找不到类,会抛出一个ActivitiException。 这个原因是部署环境(更确切是的classpath)和真实环境往往是不同的。 比如当使用ant或业务归档上传到Activiti Explorer来发布流程 classpath没有包含引用的类。
属性注入
可以为代理类的属性注入数据。支持如下类型的注入:
- 固定的字符串
- 表达式
如果有效的话,数值会通过代理类的setter方法注入,遵循java bean的命名规范(比如fistName属性对应setFirstName(...)方法)。如果属性没有对应的setter方法,数值会直接注入到私有属性中。一些环境的SecurityManager不允许修改私有属性,所以最好还是把你想注入的属性暴露出对应的setter方法来。 无论流程定义中的数据是什么类型,注入目标的属性类型都应该是 org.activiti.engine.delegate.Expression。
下面代码演示了如何把一个常量注入到属性中。 属性注入可以使用'class'属性。 注意我们需要定义一个'extensionElements' XML元素, 在声明实际的属性注入之前,这是BPMN 2.0 XML格式要求的。
<serviceTask id="javaService"
name="Java service invocation"
activiti:class="org.activiti.examples.bpmn.servicetask.ToUpperCaseFieldInjected">
<extensionElements>
<activiti:field name="text" stringValue="Hello World" />
</extensionElements>
</serviceTask>
ToUpperCaseFieldInjected类有一个text属性, 类型是org.activiti.engine.delegate.Expression。 调用text.getValue(execution)时,会返回定义的字符串Hello World。
也可以使用长文字(比如,内嵌的email),可以使用'activiti:string'子元素:
<serviceTask id="javaService"
name="Java service invocation"
activiti:class="org.activiti.examples.bpmn.servicetask.ToUpperCaseFieldInjected">
<extensionElements>
<activiti:field name="text">
<activiti:string>
Hello World
</activiti:string>
</activiti:field>
</extensionElements>
</serviceTask>
可以使用表达式,实现在运行期动态解析注入的值。这些表达式可以使用流程变量或spring定义的bean(如果使用了spring)。 像服务任务实现里说的那样,服务任务中的java类实例会在所有流程实例中共享。 为了动态注入属性的值,我们可以在org.activiti.engine.delegate.Expression中使用值和方法表达式, 它会使用传递给execute方法的DelegateExecution参数进行解析。
<serviceTask id="javaService" name="Java service invocation"
activiti:class="org.activiti.examples.bpmn.servicetask.ReverseStringsFieldInjected">
<extensionElements>
<activiti:field name="text1">
<activiti:expression>${genderBean.getGenderString(gender)}</activiti:expression>
</activiti:field>
<activiti:field name="text2">
<activiti:expression>Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}</activiti:expression>
</activiti:field>
</extensionElements>
</serviceTask>
下面的例子中,注入了表达式,并使用在传入的当前DelegateExecution解析它们。
public class ReverseStringsFieldInjected implements JavaDelegate {
private Expression text1;
private Expression text2;
public void execute(DelegateExecution execution) {
String value1 = (String) text1.getValue(execution);
execution.setVariable("var1", new StringBuffer(value1).reverse().toString());
String value2 = (String) text2.getValue(execution);
execution.setVariable("var2", new StringBuffer(value2).reverse().toString());
}
}
另外,你也可以把表达式设置成一个属性,而不是字元素,让XML更简单一些。
<activiti:field name="text1" expression="${genderBean.getGenderString(gender)}" />
<activiti:field name="text1" expression="Hello ${gender == 'male' ? 'Mr.' : 'Mrs.'} ${name}" />
注意:因为java类实例会被重用,注入只会发生一次,当服务任务调用第一次的时候。 当你的代码中的属性改变了,值也不会重新注入, 所以你应该把它们看做是不变的,不用修改它们。
服务任务结果
服务流程返回的结果(使用表达式的服务任务)可以分配给已经存在的或新的流程变量, 可以通过指定服务任务定义的'activiti:resultVariable'属性来实现。 指定的路程比那两的值会被服务流程的返回结果覆盖。 如果没有指定返回变量名,就会忽略返回结果。
<serviceTask id="aMethodExpressionServiceTask"
activiti:expression="#{myService.doSomething()}"
activiti:resultVariable="myVar" />
在上面的例子中,服务流程的返回值(在'myService'上调用'doSomething()'方法的返回值, myService可能是流程变量,也可能是spring的bean),会设置到名为'myVar'的流程变量里, 在服务执行完成之后。