java?工作流引擎設(shè)計(jì)實(shí)現(xiàn)解析流程定義文件
引言
在上一篇我們手動(dòng)構(gòu)建了一個(gè)流程對象并簡單打印執(zhí)行,其構(gòu)建流程對象的方式并不是很友好。為了更方便的構(gòu)建流程對象,我們采用全新的方式,即解析基礎(chǔ)篇提到的流程定義文件,并將其轉(zhuǎn)成流程模型。 以下是要解析的樣例文件:
src/test/resources/leave.json
{ "name": "leave", "displayName": "請假", "instanceUrl": "leaveForm", "nodes": [ { "id": "start", "type": "snaker:start", "x": 340, "y": 160, "properties": { "width": "120", "height": "80" }, "text": { "x": 340, "y": 200, "value": "開始" } }, { "id": "apply", "type": "snaker:task", "x": 520, "y": 160, "properties": { "assignee": "approve.operator", "taskType": "Major", "performType": "ANY", "autoExecute": "N", "width": "120", "height": "80", "field": { "userKey": "1" } }, "text": { "x": 520, "y": 160, "value": "請假申請" } }, { "id": "approveDept", "type": "snaker:task", "x": 740, "y": 160, "properties": { "assignmentHandler": "com.mldong.config.FlowAssignmentHandler", "taskType": "Major", "performType": "ANY", "autoExecute": "N", "width": "120", "height": "80" }, "text": { "x": 740, "y": 160, "value": "部門領(lǐng)導(dǎo)審批" } }, { "id": "end", "type": "snaker:end", "x": 980, "y": 160, "properties": { "width": "120", "height": "80" }, "text": { "x": 980, "y": 200, "value": "結(jié)束" } } ], "edges": [ { "id": "t1", "type": "snaker:transition", "sourceNodeId": "start", "targetNodeId": "apply", "startPoint": { "x": 358, "y": 160 }, "endPoint": { "x": 460, "y": 160 }, "properties": { "height": 80, "width": 120 }, "pointsList": [ { "x": 358, "y": 160 }, { "x": 460, "y": 160 } ] }, { "id": "t2", "type": "snaker:transition", "sourceNodeId": "apply", "targetNodeId": "approveDept", "startPoint": { "x": 580, "y": 160 }, "endPoint": { "x": 680, "y": 160 }, "properties": { "height": 80, "width": 120 }, "pointsList": [ { "x": 580, "y": 160 }, { "x": 680, "y": 160 } ] }, { "id": "t3", "type": "snaker:transition", "sourceNodeId": "approveDept", "targetNodeId": "end", "startPoint": { "x": 800, "y": 160 }, "endPoint": { "x": 962, "y": 160 }, "properties": { "height": 80, "width": 120 }, "pointsList": [ { "x": 800, "y": 160 }, { "x": 830, "y": 160 }, { "x": 830, "y": 160 }, { "x": 932, "y": 160 }, { "x": 932, "y": 160 }, { "x": 962, "y": 160 } ] } ] }
類圖
流程圖
代碼實(shí)現(xiàn)
model/logicflow/LfPoint.java
package com.mldong.flow.engine.model.logicflow; import lombok.Data; import java.io.Serializable; /** * * logicFlow坐標(biāo) * @author mldong * @date 2023/4/26 */ @Data public class LfPoint implements Serializable { private int x; // x軸坐標(biāo) private int y; // y軸坐標(biāo) }
LogicFlow模型對象
model/logicflow/LfNode.java
package com.mldong.flow.engine.model.logicflow; import cn.hutool.core.lang.Dict; import lombok.Data; import java.io.Serializable; /** * * logicFlow節(jié)點(diǎn) * @author mldong * @date 2023/4/26 */ @Data public class LfNode implements Serializable { private String id; // 節(jié)點(diǎn)唯一id private String type; // 節(jié)點(diǎn)類型 private int x; // 節(jié)點(diǎn)中心點(diǎn)x軸坐標(biāo) private int y; // 節(jié)點(diǎn)中心點(diǎn)y軸坐標(biāo) Dict properties; // 節(jié)點(diǎn)屬性 Dict text; // 節(jié)點(diǎn)文本 }
model/logicflow/LfEdge.java
package com.mldong.flow.engine.model.logicflow; import cn.hutool.core.lang.Dict; import lombok.Data; import java.io.Serializable; import java.util.List; /** * * LogicFlow邊 * @author mldong * @date 2022/6/12 */ @Data public class LfEdge implements Serializable { private String id; // 邊唯一id private String type; // 邊類型 private String sourceNodeId; // 源節(jié)點(diǎn)id private String targetNodeId; // 目標(biāo)節(jié)點(diǎn)id private Dict properties; // 邊屬性 private Dict text; // 邊文本 private LfPoint startPoint; // 邊開始點(diǎn)坐標(biāo) private LfPoint endPoint; // 邊結(jié)束點(diǎn)坐標(biāo) private List<LfPoint> pointsList; // 邊所有點(diǎn)集合 }
model/logicflow/LfModel.java
package com.mldong.flow.engine.model.logicflow; import com.mldong.flow.engine.model.BaseModel; import lombok.Data; import java.util.List; /** * * logicFlow模型 * @author mldong * @date 2023/4/26 */ @Data public class LfModel extends BaseModel { private String type; // 流程定義分類 private String expireTime;// 過期時(shí)間(常量或變量) private String instanceUrl; // 啟動(dòng)實(shí)例的url,前后端分離后,定義為路由名或或路由地址 private String instanceNoClass; // 啟動(dòng)流程時(shí),流程實(shí)例的流水號生成類 private List<LfNode> nodes; // 節(jié)點(diǎn)集合 private List<LfEdge> edges; // 邊集合 }
解析類
parser/NodeParser.java
package com.mldong.flow.engine.parser; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.logicflow.LfEdge; import com.mldong.flow.engine.model.logicflow.LfNode; import java.util.List; /** * * 節(jié)點(diǎn)解析接口 * @author mldong * @date 2023/4/26 */ public interface NodeParser { String NODE_NAME_PREFIX="snaker:"; // 節(jié)點(diǎn)名稱前輟 String TEXT_VALUE_KEY = "value"; // 文本值 String WIDTH_KEY = "width"; // 節(jié)點(diǎn)寬度 String HEIGHT_KEY = "height"; // 節(jié)點(diǎn)高度 String PRE_INTERCEPTORS_KEY = "preInterceptors"; // 前置攔截器 String POST_INTERCEPTORS_KEY = "postInterceptors"; // 后置攔截器 String EXPR_KEY = "expr"; // 表達(dá)式key String HANDLE_CLASS_KEY = "handleClass"; // 表達(dá)式處理類 String FORM_KEY = "form"; // 表單標(biāo)識 String ASSIGNEE_KEY = "assignee"; // 參與人 String ASSIGNMENT_HANDLE_KEY = "assignmentHandler"; // 參與人處理類 String TASK_TYPE_KEY = "taskType"; // 任務(wù)類型(主辦/協(xié)辦) String PERFORM_TYPE_KEY = "performType"; // 參與類型(普通參與/會簽參與) String REMINDER_TIME_KEY = "reminderTime"; // 提醒時(shí)間 String REMINDER_REPEAT_KEY = "reminderRepeat"; // 重復(fù)提醒間隔 String EXPIRE_TIME_KEY = "expireTime"; // 期待任務(wù)完成時(shí)間變量key String AUTH_EXECUTE_KEY = "autoExecute"; // 到期是否自動(dòng)執(zhí)行Y/N String CALLBACK_KEY = "callback"; // 自動(dòng)執(zhí)行回調(diào)類 String EXT_FIELD_KEY = "field"; // 自定義擴(kuò)展屬性 /** * 節(jié)點(diǎn)屬性解析方法,由解析類完成解析 * @param lfNode LogicFlow節(jié)點(diǎn)對象 * @param edges 所有邊對象 */ void parse(LfNode lfNode, List<LfEdge> edges); /** * 解析完成后,提供返回NodeModel對象 * @return 節(jié)點(diǎn)模型 */ NodeModel getModel(); }
parser/AbstractNodeParser.java
package com.mldong.flow.engine.parser; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.TransitionModel; import com.mldong.flow.engine.model.logicflow.LfEdge; import com.mldong.flow.engine.model.logicflow.LfNode; import java.util.List; import java.util.stream.Collectors; /** * * 通用屬性解析(基本屬性和邊) * @author mldong * @date 2023/4/26 */ public abstract class AbstractNodeParser implements NodeParser { // 節(jié)點(diǎn)模型對象 protected NodeModel nodeModel; @Override public void parse(LfNode lfNode, List<LfEdge> edges) { nodeModel = newModel(); // 解析基本信息 nodeModel.setName(lfNode.getId()); if(ObjectUtil.isNotNull(lfNode.getText())) { nodeModel.setDisplayName(lfNode.getText().getStr(TEXT_VALUE_KEY)); } Dict properties = lfNode.getProperties(); // 解析布局屬性 int x = lfNode.getX(); int y = lfNode.getY(); int w = Convert.toInt(properties.get(WIDTH_KEY),0); int h = Convert.toInt(properties.get(HEIGHT_KEY),0); nodeModel.setLayout(StrUtil.format("{},{},{},{}",x,y,w,h)); // 解析攔截器 nodeModel.setPreInterceptors(properties.getStr(PRE_INTERCEPTORS_KEY)); nodeModel.setPostInterceptors(properties.getStr(POST_INTERCEPTORS_KEY)); // 解析輸出邊 List<LfEdge> nodeEdges = getEdgeBySourceNodeId(lfNode.getId(), edges); nodeEdges.forEach(edge->{ TransitionModel transitionModel = new TransitionModel(); transitionModel.setName(edge.getId()); transitionModel.setTo(edge.getTargetNodeId()); transitionModel.setSource(nodeModel); transitionModel.setExpr(edge.getProperties().getStr(EXPR_KEY)); if(CollectionUtil.isNotEmpty(edge.getPointsList())) { // x1,y1;x2,y2;x3,y3…… transitionModel.setG(edge.getPointsList().stream().map(point->{ return point.getX()+","+point.getY(); }).collect(Collectors.joining(";"))); } else { if(ObjectUtil.isNotNull(edge.getStartPoint()) && ObjectUtil.isNotNull(edge.getEndPoint())) { int startPointX = edge.getStartPoint().getX(); int startPointY = edge.getStartPoint().getY(); int endPointX = edge.getEndPoint().getX(); int endPointY = edge.getEndPoint().getY(); transitionModel.setG(StrUtil.format("{},{};{},{}", startPointX, startPointY, endPointX, endPointY)); } } nodeModel.getOutputs().add(transitionModel); }); // 調(diào)用子類特定解析方法 parseNode(lfNode); } /** * 子類實(shí)現(xiàn)此類完成特定解析 * @param lfNode */ public abstract void parseNode(LfNode lfNode); /** * 由子類各自創(chuàng)建節(jié)點(diǎn)模型對象 * @return */ public abstract NodeModel newModel(); @Override public NodeModel getModel() { return nodeModel; } /** * 獲取節(jié)點(diǎn)輸入 * @param targetNodeId 目標(biāo)節(jié)點(diǎn)id * @param edges * @return */ private List<LfEdge> getEdgeByTargetNodeId(String targetNodeId,List<LfEdge> edges) { return edges.stream().filter(edge->{ return edge.getTargetNodeId().equals(targetNodeId); }).collect(Collectors.toList()); } /** * 獲取節(jié)點(diǎn)輸出 * @param sourceNodeId 源節(jié)點(diǎn)id * @param edges * @return */ private List<LfEdge> getEdgeBySourceNodeId(String sourceNodeId,List<LfEdge> edges) { return edges.stream().filter(edge->{ return edge.getSourceNodeId().equals(sourceNodeId); }).collect(Collectors.toList()); } }
parser/impl/StartParser.java
package com.mldong.flow.engine.parser.impl; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.StartModel; import com.mldong.flow.engine.model.logicflow.LfNode; import com.mldong.flow.engine.parser.AbstractNodeParser; /** * * 開始節(jié)點(diǎn)解析類 * @author mldong * @date 2023/4/26 */ public class StartParser extends AbstractNodeParser { @Override public void parseNode(LfNode lfNode) { } @Override public NodeModel newModel() { return new StartModel(); } }
parser/impl/EndParser.java
package com.mldong.flow.engine.parser.impl; import com.mldong.flow.engine.model.EndModel; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.logicflow.LfNode; import com.mldong.flow.engine.parser.AbstractNodeParser; /** * * 結(jié)束節(jié)點(diǎn)解析類 * @author mldong * @date 2023/4/26 */ public class EndParser extends AbstractNodeParser { @Override public void parseNode(LfNode lfNode) { } @Override public NodeModel newModel() { return new EndModel(); } }
parser/impl/TaskParser.java
package com.mldong.flow.engine.parser.impl; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Dict; import com.mldong.flow.engine.enums.TaskPerformTypeEnum; import com.mldong.flow.engine.enums.TaskTypeEnum; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.TaskModel; import com.mldong.flow.engine.model.logicflow.LfNode; import com.mldong.flow.engine.parser.AbstractNodeParser; /** * * 任務(wù)節(jié)點(diǎn)解析類 * @author mldong * @date 2023/4/26 */ public class TaskParser extends AbstractNodeParser { /** * 解析task節(jié)點(diǎn)特有屬性 * @param lfNode */ @Override public void parseNode(LfNode lfNode) { TaskModel taskModel = (TaskModel)nodeModel; Dict properties = lfNode.getProperties(); taskModel.setForm(properties.getStr(FORM_KEY)); taskModel.setAssignee(properties.getStr(ASSIGNEE_KEY)); taskModel.setAssignmentHandler(properties.getStr(ASSIGNMENT_HANDLE_KEY)); taskModel.setTaskType(TaskTypeEnum.codeOf(properties.getInt(TASK_TYPE_KEY))); taskModel.setPerformType(TaskPerformTypeEnum.codeOf(properties.getInt(PERFORM_TYPE_KEY))); taskModel.setReminderTime(properties.getStr(REMINDER_TIME_KEY)); taskModel.setReminderRepeat(properties.getStr(REMINDER_REPEAT_KEY)); taskModel.setExpireTime(properties.getStr(EXPIRE_TIME_KEY)); taskModel.setAutoExecute(properties.getStr(AUTH_EXECUTE_KEY)); taskModel.setCallback(properties.getStr(CALLBACK_KEY)); // 自定義擴(kuò)展屬性 Object field = properties.get(EXT_FIELD_KEY); if(field!=null) { taskModel.setExt(Convert.convert(Dict.class, field)); } } @Override public NodeModel newModel() { return new TaskModel(); } }
parser/impl/ForkParser.java
package com.mldong.flow.engine.parser.impl; import com.mldong.flow.engine.model.ForkModel; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.logicflow.LfNode; import com.mldong.flow.engine.parser.AbstractNodeParser; /** * * 分支節(jié)點(diǎn)解析類 * @author mldong * @date 2023/4/26 */ public class ForkParser extends AbstractNodeParser { @Override public void parseNode(LfNode lfNode) { } @Override public NodeModel newModel() { return new ForkModel(); } }
parser/impl/JoinParser.java
package com.mldong.flow.engine.parser.impl; import com.mldong.flow.engine.model.JoinModel; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.logicflow.LfNode; import com.mldong.flow.engine.parser.AbstractNodeParser; /** * * 合并節(jié)點(diǎn)解析器 * @author mldong * @date 2023/4/26 */ public class JoinParser extends AbstractNodeParser { @Override public void parseNode(LfNode lfNode) { } @Override public NodeModel newModel() { return new JoinModel(); } }
parser/impl/DecisionParser.java
package com.mldong.flow.engine.parser.impl; import cn.hutool.core.lang.Dict; import com.mldong.flow.engine.model.DecisionModel; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.logicflow.LfNode; import com.mldong.flow.engine.parser.AbstractNodeParser; /** * * 決策節(jié)點(diǎn)解析類 * @author mldong * @date 2023/4/26 */ public class DecisionParser extends AbstractNodeParser { /** * 解析decision節(jié)點(diǎn)特有屬性 * @param lfNode */ @Override public void parseNode(LfNode lfNode) { DecisionModel decisionModel = (DecisionModel) nodeModel; Dict properties = lfNode.getProperties(); decisionModel.setExpr(properties.getStr(EXPR_KEY)); decisionModel.setHandleClass(properties.getStr(HANDLE_CLASS_KEY)); } @Override public NodeModel newModel() { return new DecisionModel(); } }
服務(wù)上下文相關(guān)類
Context.java
package com.mldong.flow.engine; import java.util.List; /** * * 服務(wù)上下文接口,類似spring的ioc * @author mldong * @date 2023/4/26 */ public interface Context { /** * 根據(jù)服務(wù)名稱、實(shí)例向服務(wù)工廠注冊 * @param name 服務(wù)名稱 * @param object 服務(wù)實(shí)例 */ void put(String name, Object object); /** * 根據(jù)服務(wù)名稱、類型向服務(wù)工廠注冊 * @param name 服務(wù)名稱 * @param clazz 類型 */ void put(String name, Class<?> clazz); /** * 判斷是否存在給定的服務(wù)名稱 * @param name 服務(wù)名稱 * @return */ boolean exist(String name); /** * 根據(jù)給定的類型查找服務(wù)實(shí)例 * @param clazz 類型 * @return */ <T> T find(Class<T> clazz); /** * 根據(jù)給定的類型查找所有此類型的服務(wù)實(shí)例 * @param clazz 類型 * @return */ <T> List<T> findList(Class<T> clazz); /** * 根據(jù)給定的服務(wù)名稱、類型查找服務(wù)實(shí)例 * @param name 服務(wù)名稱 * @param clazz 類型 * @return */ <T> T findByName(String name, Class<T> clazz); }
impl/SimpleContext.java
package com.mldong.flow.engine.impl; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; import com.mldong.flow.engine.Context; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * * 簡單的上下文發(fā)現(xiàn)實(shí)現(xiàn)類 * @author mldong * @date 2023/4/26 */ public class SimpleContext implements Context { private Dict dict = Dict.create(); @Override public void put(String name, Object object) { dict.put(name, object); } @Override public void put(String name, Class<?> clazz) { dict.put(name, ReflectUtil.newInstance(clazz)); } @Override public boolean exist(String name) { return ObjectUtil.isNotNull(dict.getObj(name)); } @Override public <T> T find(Class<T> clazz) { for (Map.Entry<String, Object> entry : dict.entrySet()) { if (clazz.isInstance(entry.getValue())) { return clazz.cast(entry.getValue()); } } return null; } @Override public <T> List<T> findList(Class<T> clazz) { List<T> res = new ArrayList<>(); for (Map.Entry<String, Object> entry : dict.entrySet()) { if (clazz.isInstance(entry.getValue())) { res.add(clazz.cast(entry.getValue())); } } return res; } @Override public <T> T findByName(String name, Class<T> clazz) { for (Map.Entry<String, Object> entry : dict.entrySet()) { if (entry.getKey().equals(name) && clazz.isInstance(entry.getValue())) { return clazz.cast(entry.getValue()); } } return null; } }
core/ServiceContext.java
package com.mldong.flow.engine.core; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ReflectUtil; import com.mldong.flow.engine.Context; import java.util.List; /** * * 單例服務(wù)上下文 * @author mldong * @date 2022/6/12 */ public class ServiceContext { private static Context context; public static void setContext(Context context) { ServiceContext.context = context; } public static void put(String name, Object object) { Assert.notNull(context,"未注冊服務(wù)上下文"); context.put(name, object); } public static void put(String name, Class<?> clazz) { Assert.notNull(context,"未注冊服務(wù)上下文"); context.put(name, ReflectUtil.newInstance(clazz)); } public static boolean exist(String name) { Assert.notNull(context,"未注冊服務(wù)上下文"); return context.exist(name); } public static <T> T find(Class<T> clazz) { Assert.notNull(context,"未注冊服務(wù)上下文"); return context.find(clazz); } public static <T> List<T> findList(Class<T> clazz) { Assert.notNull(context,"未注冊服務(wù)上下文"); return context.findList(clazz); } public static <T> T findByName(String name, Class<T> clazz) { Assert.notNull(context,"未注冊服務(wù)上下文"); return context.findByName(name, clazz); } }
解析入口類
parser/ModelParser.java
package com.mldong.flow.engine.parser; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.json.JSONUtil; import com.mldong.flow.engine.core.ServiceContext; import com.mldong.flow.engine.model.NodeModel; import com.mldong.flow.engine.model.ProcessModel; import com.mldong.flow.engine.model.TaskModel; import com.mldong.flow.engine.model.TransitionModel; import com.mldong.flow.engine.model.logicflow.LfEdge; import com.mldong.flow.engine.model.logicflow.LfModel; import com.mldong.flow.engine.model.logicflow.LfNode; import java.io.ByteArrayInputStream; import java.util.List; public class ModelParser { private ModelParser(){} /** * 將json定義文件解析成流程模型對象 * @param bytes * @return */ public static ProcessModel parse(byte [] bytes) { String json = IoUtil.readUtf8(new ByteArrayInputStream(bytes)); LfModel lfModel = JSONUtil.parse(json).toBean(LfModel.class); ProcessModel processModel = new ProcessModel(); List<LfNode> nodes = lfModel.getNodes(); List<LfEdge> edges = lfModel.getEdges(); if(CollectionUtil.isEmpty(nodes) || CollectionUtil.isEmpty(edges) ) { return processModel; } // 流程定義基本信息 processModel.setName(lfModel.getName()); processModel.setDisplayName(lfModel.getDisplayName()); processModel.setType(lfModel.getType()); processModel.setInstanceUrl(lfModel.getInstanceUrl()); processModel.setInstanceNoClass(lfModel.getInstanceNoClass()); // 流程節(jié)點(diǎn)信息 nodes.forEach(node->{ String type = node.getType().replace(NodeParser.NODE_NAME_PREFIX,""); NodeParser nodeParser = ServiceContext.findByName(type,NodeParser.class); if(nodeParser!=null) { nodeParser.parse(node, edges); NodeModel nodeModel = nodeParser.getModel(); processModel.getNodes().add(nodeParser.getModel()); if (nodeModel instanceof TaskModel) { processModel.getTasks().add((TaskModel) nodeModel); } } }); // 循環(huán)節(jié)點(diǎn)模型,構(gòu)造輸入邊、輸出邊的source、target for(NodeModel node : processModel.getNodes()) { for(TransitionModel transition : node.getOutputs()) { String to = transition.getTo(); for(NodeModel node2 : processModel.getNodes()) { if(to.equalsIgnoreCase(node2.getName())) { node2.getInputs().add(transition); transition.setTarget(node2); } } } } return processModel; } }
配置類
cfg/Configuration.java
package com.mldong.flow.engine.cfg; import com.mldong.flow.engine.Context; import com.mldong.flow.engine.core.ServiceContext; import com.mldong.flow.engine.impl.SimpleContext; import com.mldong.flow.engine.parser.impl.*; public class Configuration { public Configuration() { this(new SimpleContext()); } public Configuration(Context context) { ServiceContext.setContext(context); ServiceContext.put("decision", DecisionParser.class); ServiceContext.put("end", EndParser.class); ServiceContext.put("fork", ForkParser.class); ServiceContext.put("join", JoinParser.class); ServiceContext.put("start", StartParser.class); ServiceContext.put("task", TaskParser.class); } }
單元測試類
ModelParserTest.java
package com.mldong.flow; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Dict; import com.mldong.flow.engine.cfg.Configuration; import com.mldong.flow.engine.core.Execution; import com.mldong.flow.engine.model.ProcessModel; import com.mldong.flow.engine.parser.ModelParser; import org.junit.Test; /** * * 模型解析單元測試 * @author mldong * @date 2023/4/26 */ public class ModelParserTest { @Test public void parseTest() { new Configuration(); ProcessModel processModel = ModelParser.parse(IoUtil.readBytes(this.getClass().getResourceAsStream("/leave.json"))); Execution execution = new Execution(); execution.setArgs(Dict.create()); processModel.getStart().execute(execution); } }
運(yùn)行結(jié)果
model:StartModel,name:start,displayName:開始,time:2023-04-26 21:32:40
model:TaskModel,name:apply,displayName:請假申請,time:2023-04-26 21:32:41
model:TaskModel,name:approveDept,displayName:部門領(lǐng)導(dǎo)審批,time:2023-04-26 21:32:42
model:EndModel,name:end,displayName:結(jié)束,time:2023-04-26 21:32:42
相關(guān)源碼 mldong-flow-demo-03
流程設(shè)計(jì)器 在線體驗(yàn)
以上就是java 工作流引擎設(shè)計(jì)實(shí)現(xiàn)解析流程定義文件的詳細(xì)內(nèi)容,更多關(guān)于java 工作流引擎的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java實(shí)現(xiàn)用戶簽到BitMap功能實(shí)現(xiàn)demo
這篇文章主要為大家介紹了java實(shí)現(xiàn)用戶簽到BitMap功能實(shí)現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11理解 MyBatis 是如何在 Spring 容器中初始化的
這篇文章主要介紹了理解 MyBatis 是如何在 Spring 容器中初始化的,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11java集合之CopyOnWriteArrayList源碼解析
這篇文章主要介紹了java集合之CopyOnWriteArrayList源碼解析,容器array是volatile修飾的,即set和get方法都是線程安全的,整個(gè)添加過程上了鎖,所以整體是通過volatile和lock來保證的線程安全,需要的朋友可以參考下2023-12-12SSM+微信小程序?qū)崿F(xiàn)物業(yè)管理系統(tǒng)及實(shí)例代碼
這篇文章主要介紹了SSM+微信小程序?qū)崿F(xiàn)物業(yè)管理系統(tǒng),ssm微信小程序物業(yè)管理系統(tǒng),有網(wǎng)站后臺管理系統(tǒng),本文通過實(shí)例代碼給大家展示系統(tǒng)的功能,需要的朋友可以參考下2022-02-02使用feign傳遞參數(shù)類型為MultipartFile的問題
這篇文章主要介紹了使用feign傳遞參數(shù)類型為MultipartFile的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03