JBPM深入解析之变量设计
JBPM深入解析之变量设计
在流程的流转的过程中,很多时候我们需要根据不同的实际情况传入一些初始化数据,以便完成我们个性化的业务需求;同时很多时候我们需要在不同的节点之间共享一些业务数据,特别是一些节点要以前一节点的输出作为输入等;变量对于流程引擎来说很重要,可以说没有变量,那么我们就不能运行时动态的设置和传入一些数据,这将极大的限制流程的灵活性!
变量类型
全局变量,所有的节点都可以获取并设置该变量的值
局部变量,只在该节点及其子节点可以获取并设置该变量的值
变量的传入
在流程定义中进行变量的定义
<process name="EndMultiple" xmlns="http://jbpm.org/4.4/jpdl">
<start g="16,96,48,48">
<transition to="get return code" name=""/>
</start>
<state g="96,94,111,52" name="get return code">
<transition g="151,60:-36,11" name="200" to="ok"/>
<transition g=":-16,-27" name="400" to="bad request"/>
<transition g="151,183:-33,-32" name="500" to="internal server error"/>
</state>
<end g="238,37,48,48" name="ok"/>
<end g="238,98,48,48" name="bad request"/>
<end g="240,160,48,48" name="internal server error"/>
<variable name="msg" init-expr="jbpm" type="string">
</variable>
<null></null>
</variable>
</process>
在启动流程流程实例的时候传入全局变量
variables.put("category", "big");
variables.put("dollars", 100000);
Execution execution = executionService.startProcessInstanceByKey("TaskVariables", variables);
在唤醒那些可外部唤醒类型的节点时候传入局部变量
variables.put("category", "small");
variables.put("lires", 923874893);
taskService.completeTask(taskId, variables)
在任务存在的情况下,可以在任务等待外部唤醒时进行局部变量的设置
variables.put("category", "small");
variables.put("lires", 923874893);
taskService.setVariables(taskId, variables);
在任何时候都可以通过执行服务传入设置全局变量
variables.put("category", "small");
variables.put("lires", 923874893);
executionService.setVariable(execution.getId(),variables)
variables.put("category", "small");
variables.put("lires", 923874893);
executionService.signalExecutionById(execution.getId(),variables);
变量架构设计
TypeSet、DefaultTypeSet
TypeSet定义了流程引擎中通过变量类型和matcher两种方式查找变量类型的接口;DefaultTypeSet对TypeSet的接口进行了实现,并定义了一个List<TypeMapping>来装载所有的变量类型的映射变量实例。
TypeSet
/**
* @author Tom Baeyens
*/
public interface TypeSet {
Type findTypeByMatch(String key, Object value);
Type findTypeByName(String typeName);
}
DefaultTypeSet
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author Tom Baeyens
*/
public class DefaultTypeSet implements TypeSet, Serializable {
private static final long serialVersionUID = 1L;
protected List<TypeMapping> typeMappings;
public Type findTypeByMatch(String key, Object value) {
if (typeMappings!=null) {
for (TypeMapping typeMapping: typeMappings) {
if (typeMapping.matches(key, value)) {
return typeMapping.getType();
}
}
}
return null;
}
public Type findTypeByName(String typeName) {
if ( (typeMappings!=null)
&& (typeName!=null)
) {
for (TypeMapping typeMapping: typeMappings) {
Type type = typeMapping.getType();
if (typeName.equals(type.getName())) {
return type;
}
}
}
return null;
}
public void addTypeMapping(TypeMapping typeMapping) {
if (typeMappings==null) {
typeMappings = new ArrayList<TypeMapping>();
}
typeMappings.add(typeMapping);
}
}
TypeSet的初始化 在配置文件中配置流程引擎中需要使用的变量的类型
Jbpm.defaut.cfg.xml引入变量类型配置的文件路径
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
<object class="org.jbpm.pvm.internal.id.DatabaseDbidGenerator">
<field name="commandService"><ref object="newTxRequiredCommandService" /></field>
</object>
<object class="org.jbpm.pvm.internal.id.DatabaseIdComposer" init="eager" />
<object class="org.jbpm.pvm.internal.el.JbpmElFactoryImpl" />
<!--定义变量配置文件的路径-->
<types resource="jbpm.variable.types.xml" />
<address-resolver />
</process-engine-context>
Jbpm.variable.types.xml定义了变量的类型、使用的转换器等信息
<!-- types stored in a native column -->
<type name="string" class="java.lang.String" variable-class="org.jbpm.pvm.internal.type.variable.StringVariable" />
<type name="long" class="java.lang.Long" variable-class="org.jbpm.pvm.internal.type.variable.LongVariable" />
<type name="double" class="java.lang.Double" variable-class="org.jbpm.pvm.internal.type.variable.DoubleVariable" />
<!-- types converted to a string -->
<type name="date" class="java.util.Date" converter="org.jbpm.pvm.internal.type.converter.DateToStringConverter" variable-class="org.jbpm.pvm.internal.type.variable.StringVariable" />
<type name="boolean" class="java.lang.Boolean" converter="org.jbpm.pvm.internal.type.converter.BooleanToStringConverter" variable-class="org.jbpm.pvm.internal.type.variable.StringVariable" />
<type name="char" class="java.lang.Character" converter="org.jbpm.pvm.internal.type.converter.CharacterToStringConverter" variable-class="org.jbpm.pvm.internal.type.variable.StringVariable" />
<!-- types converted to a long -->
<type name="byte" class="java.lang.Byte" converter="org.jbpm.pvm.internal.type.converter.ByteToLongConverter" variable-class="org.jbpm.pvm.internal.type.variable.LongVariable" />
<type name="short" class="java.lang.Short" converter="org.jbpm.pvm.internal.type.converter.ShortToLongConverter" variable-class="org.jbpm.pvm.internal.type.variable.LongVariable" />
<type name="integer" class="java.lang.Integer" converter="org.jbpm.pvm.internal.type.converter.IntegerToLongConverter" variable-class="org.jbpm.pvm.internal.type.variable.LongVariable" />
<!-- types converted to a double -->
<type name="float" class="java.lang.Float" converter="org.jbpm.pvm.internal.type.converter.FloatToDoubleConverter" variable-class="org.jbpm.pvm.internal.type.variable.DoubleVariable" />
<!-- byte[] and char[] -->
<type name="byte[]" class="[B" variable-class="org.jbpm.pvm.internal.type.variable.BlobVariable" />
<type name="char[]" class="[C" variable-class="org.jbpm.pvm.internal.type.variable.TextVariable" />
<type name="hibernate-long-id" class="hibernatable" id-type="long" variable-class="org.jbpm.pvm.internal.type.variable.HibernateLongVariable" />
<type name="hibernate-string-id" class="hibernatable" id-type="string" variable-class="org.jbpm.pvm.internal.type.variable.HibernateStringVariable" />
<type name="serializable" class="serializable" converter="org.jbpm.pvm.internal.type.converter.SerializableToBytesConverter" variable-class="org.jbpm.pvm.internal.type.variable.BlobVariable" />
<!-- TODO: add ejb3 entity bean support -->
<!-- TODO: add JCR activity support -->
<!-- TODO: add collection support -->
</types>
TypesBinding解析jbpm.variable.types.xml中的变量类型定义
public TypesBinding() {
super("types");
}
public Object parse(Element element, Parse parse, Parser parser) {
StreamInput streamSource = null;
//查找type文件
if (element.hasAttribute("file")) {
String fileName = element.getAttribute("file");
File file = new File(fileName);
if (file.exists() && file.isFile()) {
streamSource = new FileStreamInput(file);
parser.importStream(streamSource, element, parse);
} else {
parse.addProblem("file "+fileName+" isn't a file", element);
}
}
if (element.hasAttribute("resource")) {
String resource = element.getAttribute("resource");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
streamSource = new ResourceStreamInput(resource, classLoader);
parser.importStream(streamSource, element, parse);
}
if (element.hasAttribute("url")) {
String urlText = element.getAttribute("url");
try {
URL url = new URL(urlText);
streamSource = new UrlStreamInput(url);
parser.importStream(streamSource, element, parse);
} catch (Exception e) {
parse.addProblem("couldn't open url "+urlText, e);
}
}
TypesDescriptor typesDescriptor = new TypesDescriptor();
List<Element> typeElements = XmlUtil.elements(element, "type");
for (Element typeElement: typeElements) {
TypeMapping typeMapping = parseTypeMapping(typeElement, parse, parser);
typesDescriptor.addTypeMapping(typeMapping);
}
return typesDescriptor;
}
protected TypeMapping parseTypeMapping(Element element, Parse parse, Parser parser) {
TypeMapping typeMapping = new TypeMapping();
Type type = new Type();
typeMapping.setType(type);
// type name
//类型名称
if (element.hasAttribute("name")) {
type.setName(element.getAttribute("name"));
}
String hibernateSessionFactoryName = XmlUtil.attribute(element, "hibernate-session-factory");
// first we get the matcher
Matcher matcher = null;
if (element.hasAttribute("class")) {
String className = element.getAttribute("class");
// if type="serializable"
if ("serializable".equals(className)) {
matcher = new SerializableMatcher();
// if type="hibernatable"
} else if ("hibernatable".equals(className)) {
if (element.hasAttribute("id-type")) {
String idType = element.getAttribute("id-type");
if ("long".equalsIgnoreCase(idType)) {
matcher = new HibernateLongIdMatcher(hibernateSessionFactoryName);
} else if ("string".equalsIgnoreCase(idType)) {
matcher = new HibernateStringIdMatcher(hibernateSessionFactoryName);
} else {
parse.addProblem("id-type was not 'long' or 'string': "+idType, element);
}
} else {
parse.addProblem("id-type is required in a persistable type", element);
}
// otherwise, we expect type="some.java.ClassName"
} else {
matcher = new ClassNameMatcher(className);
}
} else {
// look for the matcher element
Element matcherElement = XmlUtil.element(element, "matcher");
Element matcherObjectElement = XmlUtil.element(matcherElement);
if (matcherObjectElement!=null) {
try {
Descriptor descriptor = (Descriptor) parser.parseElement(matcherObjectElement, parse);
matcher = (Matcher) WireContext.create(descriptor);
} catch (ClassCastException e) {
parse.addProblem("matcher is not a "+Matcher.class.getName()+": "+(matcher!=null ? matcher.getClass().getName() : "null"), element);
}
} else {
parse.addProblem("no matcher specified in "+XmlUtil.toString(element), element);
}
}
typeMapping.setMatcher(matcher);
// parsing the converter
Converter converter = null;
if (element.hasAttribute("converter")) {
String converterClassName = element.getAttribute("converter");
try {
Class<?> converterClass = ReflectUtil.classForName(converterClassName);
converter = (Converter) converterClass.newInstance();
} catch (Exception e) {
parse.addProblem("couldn't instantiate converter "+converterClassName, element);
}
} else {
// look for the matcher element
Element converterElement = XmlUtil.element(element, "converter");
Element converterObjectElement = XmlUtil.element(converterElement);
if (converterObjectElement!=null) {
try {
converter = (Converter) parser.parseElement(converterObjectElement, parse);
} catch (ClassCastException e) {
parse.addProblem("converter is not a "+Converter.class.getName()+": "+(converter!=null ? converter.getClass().getName() : "null"), element);
}
}
}
type.setConverter(converter);
// parsing the variable class
Class<?> variableClass = null;
if (element.hasAttribute("variable-class")) {
String variableClassName = element.getAttribute("variable-class");
try {
variableClass = ReflectUtil.classForName(variableClassName);
} catch (Exception e) {
parse.addProblem("couldn't instantiate variable-class "+variableClassName, e);
}
} else {
parse.addProblem("variable-class is required on a type: "+XmlUtil.toString(element), element);
}
type.setVariableClass(variableClass);
return typeMapping;
}
}
TypeDescriptor用于运行时生成DefaultTypeSet
private static final long serialVersionUID = 1L;
DefaultTypeSet defaultTypeSet = new DefaultTypeSet();
public Object construct(WireContext wireContext) {
return defaultTypeSet;
}
public Class< ? > getType(WireDefinition wireDefinition) {
return DefaultTypeSet.class;
}
public void addTypeMapping(TypeMapping typeMapping) {
defaultTypeSet.addTypeMapping(typeMapping);
}
public DefaultTypeSet getDefaultTypeSet() {
return defaultTypeSet;
}
}
TypeMapping作为映射器,负责承载变量的类型和matcher
Matcher matcher;
Type type;
private static final long serialVersionUID = 1L;
public boolean matches(String name, Object value) {
return matcher.matches(name, value);
}
public String toString() {
return "("+matcher+"-->"+type+")";
}
public void setMatcher(Matcher matcher) {
this.matcher = matcher;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public Matcher getMatcher() {
return matcher;
}
}
Matcher,递归查询给定变量的类型以及所有的基类中是否有与该Matcher对应的类的变量类型相同的。
private static final long serialVersionUID = 1L;
String className = null;
public ClassNameMatcher(String className) {
this.className = className;
}
public String toString() {
return className;
}
public boolean matches(String name, Object value) {
if (value==null) {
return true;
}
Class<?> valueClass = value.getClass();
while (valueClass!=null) {
if (className.equals(value.getClass().getName())) {
return true;
} else {
Class<?>[] interfaces = valueClass.getInterfaces();
for (int i=0; i<interfaces.length; i++) {
if (className.equals(interfaces[i].getName())) {
return true;
}
}
valueClass = valueClass.getSuperclass();
}
}
return false;
}
}
Type 定义变量类型的名称、转换器和对应的Variable
private static final long serialVersionUID = 1L;
protected String name;
protected Converter converter;
protected Class<?> variableClass;
public String toString() {
if (name!=null) {
return name;
}
StringBuilder text = new StringBuilder();
if (converter!=null) {
text.append(converter.toString());
text.append("-->");
}
if (variableClass!=null) {
text.append(variableClass.getSimpleName());
} else {
text.append("undefined");
}
return text.toString();
}
public Converter getConverter() {
return converter;
}
public void setConverter(Converter converter) {
this.converter = converter;
}
public Class< ? > getVariableClass() {
return variableClass;
}
public void setVariableClass(Class< ? > variableClass) {
this.variableClass = variableClass;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Converter,负责变量运行时的表现形式和持久化形式进行转化
private static final long serialVersionUID = 1L;
public boolean supports(Object value, ScopeInstanceImpl scopeInstance, Variable variable) {
if (value==null) return true;
return Serializable.class.isAssignableFrom(value.getClass());
}
public Object convert(Object o, ScopeInstanceImpl scopeInstance, Variable variable) {
byte[] bytes = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.flush();
bytes = baos.toByteArray();
Transaction transaction = EnvironmentImpl.getFromCurrent(Transaction.class, false);
if (transaction!=null) {
transaction.registerDeserializedObject(new DeserializedObject(o, scopeInstance, (BlobVariable) variable));
}
} catch (IOException e) {
throw new JbpmException("couldn't serialize '"+o+"'", e);
}
return bytes;
}
public Object revert(Object o, ScopeInstanceImpl scopeInstance, Variable variable) {
byte[] bytes = (byte[]) o;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = null;
ois = new DeploymentObjectInputStream(bais, scopeInstance.getExecution().getProcessDefinition().getDeploymentId());
Object object = ois.readObject();
Transaction transaction = EnvironmentImpl.getFromCurrent(Transaction.class, false);
if (transaction!=null) {
transaction.registerDeserializedObject(new DeserializedObject(object, scopeInstance, (BlobVariable) variable));
}
return object;
} catch (Exception e) {
throw new JbpmException("couldn't deserialize object", e);
}
}
}
Variable,定义具体的变量以及一些持久化需要的属性
private static final long serialVersionUID = 1L;
protected String string = null;
public boolean isStorable(Object value) {
if (value==null) return true;
return (String.class==value.getClass());
}
public Object getObject() {
return string;
}
public void setObject(Object value) {
this.string = (String) value;
if (value!=null) {
this.textValue = string;
} else {
this.textValue = null;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现