流程引擎实现(一)—串行流程实现

背景

在传统的MVC架构中,和业务逻辑相关的代码一般是编写在service层,但随着业务的发展,service层会不断充斥各种逻辑,导致service层过于臃肿、庞大,此外,职责定义不够清晰,如何对service层进行瘦身,以达到职责分离的效果,成为后续开发中不断需要面对的一个难题。

通过流程引擎,将service层的各种业务逻辑,拆分到不同的activitiy节点中,从而达到职责分离的效果,此外,通过对这些activity节点进行编排,能有效进行流程追踪和监控,以及实现流程标准化。

目前主流的开源流程引擎有activiti、flowable、camunda,在我的实际工作项目中,也用到了流程引擎,基于对流程引擎的好奇和兴趣,决定自己实现一个简单的流程引擎,一方面是锻炼自己的编程能力,另一方面是加深自己对流程引擎的一些理解和认识,下面是具体的实现思路。

流程引擎解析

无论是什么类型的流程引擎,在执行的时候,都是依据配置文件中配置的流程执行顺序,挨个执行相应的流程节点,通过这些流程节点的活动和编排的顺序,完成相关的业务逻辑,从而对外提供服务。

首先我们定义流程文件解析接口:

1
2
3
public interface IActivitiFileParser {
ActivitiFlow parseActivitiFlow(String file);
}

以注册流程为例,假设我们的流程配置文件定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<activiti id="register_flow">
<!--节点定义-->
<startEvent id="startEvent"/>
<serviceTask id="registerFormCheck" name="registerFormCheck" description="注册表单校验" class="com.yang.application.register.activiti.RegisterFormCheckActivity"/>
<serviceTask id="userConflictCheck" name="userConflictCheck" description="用户冲突定位" class="com.yang.application.register.activiti.UserConflictCheckActivity"/>
<serviceTask id="encryptPassword" name="encryptPassword" description="密码加密" class="com.yang.application.register.activiti.EncryptPasswordActivity"/>
<serviceTask id="userRoleRich" name="userRoleRich" description="用户权限填充" class="com.yang.application.register.activiti.UserRoleRichActivity"/>
<serviceTask id="createUser" name="createUser" description="创建用户" class="com.yang.application.register.activiti.CreateUserActivity"/>
<serviceTask id="buildUserToken" name="buildUserToken" description="创建用户token" class="com.yang.application.register.activiti.BuildUserTokenActivity"/>
<endEvent id="endEvent"/>

<!--节点执行顺序编排-->
<sequenceFlow source="startEvent" target="registerFormCheck"/>
<sequenceFlow source="registerFormCheck" target="userConflictCheck"/>
<sequenceFlow source="userConflictCheck" target="encryptPassword"/>
<sequenceFlow source="encryptPassword" target="userRoleRich"/>
<sequenceFlow source="userRoleRich" target="createUser"/>
<sequenceFlow source="createUser" target="buildUserToken"/>
<sequenceFlow source="buildUserToken" target="endEvent"/>
</activiti>

这里,我们将流程节点分为下列四类:

类型 定义
startEvent 开始事件
serviceTask 服务任务,执行对应的业务逻辑活动
endEvent 结束事件
sequenceFlow 流程线,用于定义流程节点之间的执行先后顺序

我们将这些类型,抽象到相关的枚举类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.yang.infrastructure.flow.activiti.enums;


public enum FlowNodeType {
START_EVENT("startEvent"),
SERVICE_TASK("serviceTask"),
END_EVENT("endEvent"),
SEQUENCE_FLOW("sequenceFlow");

private String code;

FlowNodeType(String code) {
this.code = code;
}

public String getCode() {
return code;
}

public static FlowNodeType parseByCode(String code) {
for (FlowNodeType value : values()) {
if (value.getCode().equals(code)) {
return value;
}
}
return null;
}
}

对于上述四种类型,可以进一步划分为两类——节点类和节点连线类,我们为此定义两种模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 节点类
@Data
public class FlowNode implements Serializable {

private String id;

private String name;

private String description;

private Object target;

private String classPath;

private FlowNodeType flowNodeType;

}


// 节点连线类
@Data
public class SequenceFlowNode implements Serializable {

private String source;

private String target;

}

然后我们定义流程文件相关的聚合类,该类有两个属性,一个是流程id(也就是流程文件名称),另一个是该流程中的流程节点(这里以List来实现,List中元素的顺序,也就是流程节点的执行顺序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public class ActivitiFlow {
private String id;

private List<FlowNode> flowNodeList = new ArrayList<>();

public void addFlowNode(FlowNode flowNode) {
this.flowNodeList.add(flowNode);
}

public List<FlowNode> getFlowNodeList() {
return this.flowNodeList;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}
}

然后对于ActivitiFlow的构造,主要是根据流程连线和流程节点来实现,对于不同的流程文件解析类,他们负责的只是流程连线、流程节点的解析,构造过程则是通用的,因此我们将其构造过程提炼为抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

public abstract class AbstractActivitiFileParser implements IActivitiFileParser{

/**
* 填充流程节点内容
* @param flowNode
* @param flowNodeType
*/
protected void richFlowNode(FlowNode flowNode, FlowNodeType flowNodeType) {
if (flowNodeType != FlowNodeType.SERVICE_TASK) {
return;
}
try {
// 填充target对象
Class<?> aClass = Class.forName(flowNode.getClassPath());
Object target = SpringContextUtil.getBeanByType(aClass);
flowNode.setTarget(target);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

/**
* 根据连线顺序,对流程节点进行排序,构建相应的流程flow
* @param fileName
* @param flowNodeList
* @param sequenceFlowNodeList
* @return
*/
protected ActivitiFlow buildActivitiFlow(String fileName,
List<FlowNode> flowNodeList,
List<SequenceFlowNode> sequenceFlowNodeList) {
List<SequenceFlowNode> sortSequenceFlowNodeList = sortSequenceFlowNodeList(sequenceFlowNodeList);
ActivitiFlow activitiFlow = new ActivitiFlow();
activitiFlow.setId(fileName);
Map<String, FlowNode> id2FlowNodeMap = flowNodeList.stream()
.collect(Collectors.toMap(FlowNode::getId, Function.identity()));
SequenceFlowNode iterator = null;
for (int i = 0; i < sortSequenceFlowNodeList.size(); i++) {
iterator = sortSequenceFlowNodeList.get(i);
String source = iterator.getSource();
FlowNode flowNode = id2FlowNodeMap.get(source);
activitiFlow.addFlowNode(flowNode);
}
FlowNode endNode = id2FlowNodeMap.get(iterator.getTarget());
activitiFlow.addFlowNode(endNode);
return activitiFlow;
}

/**
* 按照开始到结束,对连线进行排序
* @param sequenceFlowNodeList
* @return
*/
private List<SequenceFlowNode> sortSequenceFlowNodeList(List<SequenceFlowNode> sequenceFlowNodeList) {
Map<String, SequenceFlowNode> source2SequenceFlowNodeMap = sequenceFlowNodeList.stream()
.collect(Collectors.toMap(SequenceFlowNode::getSource, Function.identity()));
Map<String, String> target2SourceMap = new HashMap<>();
for (SequenceFlowNode sequenceFlowNode : sequenceFlowNodeList) {
target2SourceMap.put(sequenceFlowNode.getTarget(), sequenceFlowNode.getSource());
}
// 定位开始节点
SequenceFlowNode startSequenceFlowNode = null;
for (SequenceFlowNode sequenceFlowNode : sequenceFlowNodeList) {
String from = target2SourceMap.get(sequenceFlowNode.getSource());
if (from == null) {
startSequenceFlowNode = sequenceFlowNode;
break;
}
}
if (startSequenceFlowNode == null) {
throw new UicException(ErrorCode.ACTIVITI_PARSER_CIRCULATION_ERROR);
}

List<SequenceFlowNode> sortSequenceFlowNodeList = new ArrayList<>();
SequenceFlowNode iterator = startSequenceFlowNode;
sortSequenceFlowNodeList.add(iterator);
while (true) {
String target = iterator.getTarget();
iterator = source2SequenceFlowNodeMap.get(target);
if (iterator == null) {
break;
}
sortSequenceFlowNodeList.add(iterator);
}
return sortSequenceFlowNodeList;
}
}

最一般流程引擎的流程编排配置文件,是xml类型的,因此这里实现一个Xml配置文件的解析类,用于解析我们的xml流程文件,具体实现内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package com.yang.infrastructure.flow.activiti.parser;

import com.yang.infrastructure.flow.activiti.enums.FlowNodeType;
import com.yang.infrastructure.flow.activiti.model.ActivitiFlow;
import com.yang.infrastructure.flow.activiti.model.FlowNode;
import com.yang.infrastructure.flow.activiti.model.SequenceFlowNode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class XmlActivitiFileParser extends AbstractActivitiFileParser {
private static final String ACTIVITI_PREFIX = "yang-engine";

@Override
public ActivitiFlow parseActivitiFlow(String fileName) {
return parseActivitiFlow(ACTIVITI_PREFIX, fileName);
}

public ActivitiFlow parseActivitiFlow(String directory, String fileName) {
String fullFileName = directory + File.separator + fileName;
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(fullFileName);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(inputStream);
// 解析出流程节点
List<FlowNode> flowNodeList = parseFlowNodeList(document);
// 解析流程执行顺序
List<SequenceFlowNode> sequenceFlowNodeList = parseSequenceFlowNodeList(document);
String activitiId = parseActivitiId(document);
if (StringUtils.isEmpty(activitiId)) {
activitiId = fileName;
}
return buildActivitiFlow(activitiId, flowNodeList, sequenceFlowNodeList);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
}
}

/**
* 解析流程id
* @param document
* @return
*/
private String parseActivitiId(Document document) {
NodeList nodeList = document.getElementsByTagName("activiti");
Node activitiNode = nodeList.item(0);
NamedNodeMap attributes = activitiNode.getAttributes();
Node id = attributes.getNamedItem("id");
if (id != null) {
return id.getNodeValue();
}
return null;
}

/**
* 解析流程节点列表
* @param document
* @return
*/
private List<FlowNode> parseFlowNodeList(Document document) {
List<FlowNode> flowNodeList = new ArrayList<>();
NodeList activitiNodeList = document.getElementsByTagName("activiti");
if (activitiNodeList.getLength() <= 0) {
return flowNodeList;
}
Node activitiNode = activitiNodeList.item(0);
NodeList nodeList = activitiNode.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
FlowNodeType flowNodeType = FlowNodeType.parseByCode(node.getNodeName());
if (flowNodeType == null || flowNodeType == FlowNodeType.SEQUENCE_FLOW) {
continue;
}
NamedNodeMap attributes = node.getAttributes();
FlowNode flowNode = parseFlowNode(attributes, flowNodeType);
flowNodeList.add(flowNode);
}
return flowNodeList;
}

/**
* 解析流程节点
* @param attributes
* @param flowNodeType
* @return
*/
private FlowNode parseFlowNode(NamedNodeMap attributes, FlowNodeType flowNodeType) {
if (attributes == null || attributes.getLength() <= 0) {
return null;
}
FlowNode flowNode = new FlowNode();
Node idNode = attributes.getNamedItem("id");
Node nameNode = attributes.getNamedItem("name");
Node descriptionNode = attributes.getNamedItem("description");
Node classNode = attributes.getNamedItem("class");
if (idNode == null) {
return null;
}
flowNode.setId(idNode.getNodeValue());
if (nameNode != null) {
flowNode.setName(nameNode.getNodeValue());
}
if (descriptionNode != null) {
flowNode.setDescription(descriptionNode.getNodeValue());
}
if (classNode != null) {
flowNode.setClassPath(classNode.getNodeValue());
}
flowNode.setFlowNodeType(flowNodeType);
richFlowNode(flowNode, flowNodeType);
return flowNode;
}

/**
* 解析流程执行顺序节点列表
* @param document
* @return
*/
private List<SequenceFlowNode> parseSequenceFlowNodeList(Document document) {
NodeList nodeList = document.getElementsByTagName(FlowNodeType.SEQUENCE_FLOW.getCode());
List<SequenceFlowNode> sequenceFlowNodes = new ArrayList<>();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
NamedNodeMap attributes = node.getAttributes();
SequenceFlowNode sequenceFlowNode = parseSequenceFlowNode(attributes);
if (sequenceFlowNode != null) {
sequenceFlowNodes.add(sequenceFlowNode);
}
}
return sequenceFlowNodes;
}

/**
* 解析流程执行顺序节点
* @param attributes
* @return
*/
private SequenceFlowNode parseSequenceFlowNode(NamedNodeMap attributes) {
Node sourceNode = attributes.getNamedItem("source");
Node targetNode = attributes.getNamedItem("target");
if (sourceNode != null && targetNode != null) {
SequenceFlowNode sequenceFlowNode = new SequenceFlowNode();
sequenceFlowNode.setSource(sourceNode.getNodeValue());
sequenceFlowNode.setTarget(targetNode.getNodeValue());
return sequenceFlowNode;
}
return null;
}

public static void main(String[] args) {
IActivitiFileParser activitiFileParser = new XmlActivitiFileParser();
ActivitiFlow activitiFlow = activitiFileParser.parseActivitiFlow("register_flow.xml");
}
}

最后添加对应的测试方法,测试上述方式是否能成功解析流程引擎配置文件:

1
2
3
4
5
6
7
8
9
@Test
public void testParseActivitiFile() {
IActivitiFileParser iActivitiFileParser = new XmlActivitiFileParser();
ActivitiFlow activitiFlow = iActivitiFileParser.parseActivitiFlow("register_flow.xml");
for (FlowNode flowNode : activitiFlow.getFlowNodeList()) {
System.out.println(flowNode);
}
System.out.println(activitiFlow);
}

解析结果如下:

懒加载流程到缓存中

创建ActivitiFlow流程管理类,在第一次获取ActivitiFlow,将其加载到缓存中,以减少后续的流程配置文件解析次数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.yang.infrastructure.flow.activiti;

import com.yang.infrastructure.common.ErrorCode;
import com.yang.infrastructure.common.exception.UicException;
import com.yang.infrastructure.flow.activiti.model.ActivitiFlow;
import com.yang.infrastructure.flow.activiti.parser.IActivitiFileParser;
import com.yang.infrastructure.utils.SpringContextUtil;

import java.util.HashMap;
import java.util.Map;

public class ActivitiFlowManager {
private static Map<String, ActivitiFlow> id2ActivitiFlowCache = new HashMap<>();

public static ActivitiFlow getActivitiFlow(String flowName) {
ActivitiFlow activitiFlow = id2ActivitiFlowCache.get(flowName);
if (activitiFlow != null) {
return activitiFlow;
}
IActivitiFileParser iActivitiFileParser = SpringContextUtil.getBeanByType(IActivitiFileParser.class);
synchronized (flowName.intern()) {
activitiFlow = id2ActivitiFlowCache.get(flowName);
if (activitiFlow != null) {
return activitiFlow;
}
activitiFlow = iActivitiFileParser.parseActivitiFlow(flowName);
if (activitiFlow == null) {
throw new UicException(ErrorCode.ACTIVITI_NOT_FOUND_ERROR);
}
id2ActivitiFlowCache.put(flowName, activitiFlow);
}
return activitiFlow;
}
}

流程引擎执行

定义流程引擎入参和出参,这里采用泛形的方式,定义对应的入参和出参,因为我们流程引擎不感知具体的业务参数类型,只需提供一个通用的底层交互协议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

@Data
public class ActivitiEngineRequest <T> implements Serializable {

/**
* 场景code
*/
private String scenarioCode;

private T baseRequest;
}



@Data
public class ActivitiEngineResponse <T> implements Serializable {
/**
* 场景code
*/
private String scenarioCode;

private T response;
}

然后我们定义流程引擎执行接口

1
2
3
public interface IActivitiService {
void execute(ActivitiEngineRequest request, ActivitiEngineResponse response);
}

对于startEvent, endEvent, serviceTask这几种流程节点类型,依次去实现对应的IActivitiService,因为目前暂时只用到serviceTask,所以下面只列出serviceTask的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

public interface IServiceTask<DomainRequest, DomainResponse> extends IActivitiService {
default void execute(ActivitiEngineRequest request, ActivitiEngineResponse response) {
DomainRequest domainRequest = buildDomainRequest(request, response);
if (domainRequest != null) {
DomainResponse domainResponse = apply(domainRequest);
attachment(domainResponse, response);
return;
}
doElse(request, response);
}

/**
* 默认不实现,一般先转为domain Request,然后调用领域层方法,进行转化,doElse适用于目前领域层无法支持相关实现,顾需要在activity层进行适配的逻辑
* @param request
* @param response
*/
default void doElse(ActivitiEngineRequest request, ActivitiEngineResponse response) {}

/**
* 入参转化
* @param request
* @param response
* @return
*/
DomainRequest buildDomainRequest(ActivitiEngineRequest request, ActivitiEngineResponse response);

/**
* 领域服务调用
* @param domainRequest
* @return
*/
DomainResponse apply(DomainRequest domainRequest);

/**
* 领域服务调用结果填充
* @param domainResponse
* @param response
*/
void attachment(DomainResponse domainResponse, ActivitiEngineResponse response);
}

在IServiceTask的流程引擎调用中,这里的实现思路是将流程引擎入参先转化为对应的领域入参,然后调用领域服务执行对应的领域操作,最后,将领域服务返回结果,转化或填充到我们的流程引擎出参当中。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

@Component
public class EncryptPasswordActivity implements IServiceTask<UserPasswordEncryptDomainRequest, UserPasswordEncryptDomainResponse> {
@Autowired
private UserPasswordDomainService userPasswordDomainService;

@Override
public UserPasswordEncryptDomainRequest buildDomainRequest(ActivitiEngineRequest request, ActivitiEngineResponse response) {
RegisterRequest registerRequest = (RegisterRequest) request.getBaseRequest();
UserPasswordEncryptDomainRequest userPasswordEncryptDomainRequest = new UserPasswordEncryptDomainRequest();

String encryptType = registerRequest.getExtendMaps().get("encryptType");
EncryptTypeEnum encryptTypeEnum = EncryptTypeEnum.parseByType(encryptType);
userPasswordEncryptDomainRequest.setEncryptType(encryptTypeEnum);
userPasswordEncryptDomainRequest.setPlainPassword(registerRequest.getPassword());
return userPasswordEncryptDomainRequest;
}

@Override
public UserPasswordEncryptDomainResponse apply(UserPasswordEncryptDomainRequest userPasswordEncryptDomainRequest) {
return userPasswordDomainService.encryptPassword(userPasswordEncryptDomainRequest);
}

@Override
public void attachment(UserPasswordEncryptDomainResponse userPasswordEncryptDomainResponse, ActivitiEngineResponse response) {
UserModel userModel = (UserModel) response.getResponse();
userModel.setEncryptPassword(userPasswordEncryptDomainResponse.getEncryptPassword());
}
}

但是这有一个缺陷,就是我们的领域服务一般都是比较原子的,当我们要执行的操作涉及多个原子操作时,上面这种思路就无法支撑,所以这里加了一个doElse来兼容,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

@Component
public class UserConflictCheckActivity implements IServiceTask<UserQueryDomainRequest, UserQueryDomainResponse> {
@Autowired
private UserDomainService userDomainService;

@Override
public UserQueryDomainRequest buildDomainRequest(ActivitiEngineRequest request, ActivitiEngineResponse response) {
return null;
}

@Override
public UserQueryDomainResponse apply(UserQueryDomainRequest userQueryDomainRequest) {
return null;
}

@Override
public void doElse(ActivitiEngineRequest request, ActivitiEngineResponse response) {
RegisterRequest registerRequest = (RegisterRequest) request.getBaseRequest();
if (StringUtils.isNotEmpty(registerRequest.getLoginId())) {
UserQueryDomainRequest userQueryDomainRequest = new UserQueryDomainRequest.UserQueryDomainRequestBuilder()
.userQueryType(UserQueryType.LOGIN_ID)
.queryMessage(registerRequest.getLoginId())
.build();
UserQueryDomainResponse userQueryDomainResponse = userDomainService.query(userQueryDomainRequest);
if (!CollectionUtils.isEmpty(userQueryDomainResponse.getUserAccountList())) {
throw new UicException(ErrorCode.USER_CONFLICT_ERROR);
}
}
UserQueryDomainRequest userQueryDomainRequest = new UserQueryDomainRequest.UserQueryDomainRequestBuilder()
.userQueryType(UserQueryType.EMAIL)
.queryMessage(registerRequest.getEmail())
.build();
UserQueryDomainResponse userQueryDomainResponse = userDomainService.query(userQueryDomainRequest);
if (!CollectionUtils.isEmpty(userQueryDomainResponse.getUserAccountList())) {
throw new UicException(ErrorCode.USER_CONFLICT_ERROR);
}
}

@Override
public void attachment(UserQueryDomainResponse userQueryDomainResponse, ActivitiEngineResponse response) {

}
}

最后,我们修改ActivitiFlowManager, 增加执行方法,执行的思路是,先解析对应的流程配置,然后根据流程节点类型,依次执行对应的流程方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

public class ActivitiFlowManager {
private static Map<String, ActivitiFlow> id2ActivitiFlowCache = new HashMap<>();

public static ActivitiFlow getActivitiFlow(String flowName) {
ActivitiFlow activitiFlow = id2ActivitiFlowCache.get(flowName);
if (activitiFlow != null) {
return activitiFlow;
}
IActivitiFileParser iActivitiFileParser = SpringContextUtil.getBeanByType(IActivitiFileParser.class);
synchronized (flowName.intern()) {
activitiFlow = id2ActivitiFlowCache.get(flowName);
if (activitiFlow != null) {
return activitiFlow;
}
activitiFlow = iActivitiFileParser.parseActivitiFlow(flowName);
if (activitiFlow == null) {
throw new UicException(ErrorCode.ACTIVITI_NOT_FOUND_ERROR);
}
id2ActivitiFlowCache.put(flowName, activitiFlow);
}
return activitiFlow;
}

/**
* 流程引擎执行方法
* @param flowName
* @param activitiEngineRequest
* @return
* @param <Request>
* @param <Response>
*/
public static <Request, Response> ActivitiEngineResponse<Response> startEngine(String flowName,
ActivitiEngineRequest<Request> activitiEngineRequest) {
ActivitiFlow activitiFlow = getActivitiFlow(flowName);
ActivitiEngineResponse<Response> activitiEngineResponse = new ActivitiEngineResponse<>();
for (FlowNode flowNode : activitiFlow.getFlowNodeList()) {
FlowNodeType flowNodeType = flowNode.getFlowNodeType();
switch (flowNodeType) {
case START_EVENT:
break;
case END_EVENT:
break;
case SERVICE_TASK:
executeServiceTask(activitiEngineRequest, activitiEngineResponse, flowNode);
break;
}
}
return activitiEngineResponse;
}

private static <Request, Response> void executeServiceTask(ActivitiEngineRequest<Request> activitiEngineRequest, ActivitiEngineResponse<Response> activitiEngineResponse, FlowNode flowNode) {
IServiceTask iServiceTask = (IServiceTask) flowNode.getTarget();
iServiceTask.execute(activitiEngineRequest, activitiEngineResponse);
}
}

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  @Override
public ResultT<String> register2(RegisterRequest registerRequest) {
ActivitiEngineRequest<RegisterRequest> activitiEngineRequest = new ActivitiEngineRequest<>();
activitiEngineRequest.setBaseRequest(registerRequest);
ActivitiEngineResponse<UserModel> activitiEngineResponse = ActivitiFlowManager.startEngine("register_flow.xml", activitiEngineRequest);
UserModel userModel = activitiEngineResponse.getResponse();
if (userModel != null && userModel.getUserToken() != null) {
String token = userModel.getUserToken().getToken();
return ResultT.success(token);
}
return ResultT.fail();
}



@Test
public void testEngineRegister() {
RegisterRequest registerRequest = new RegisterRequest();
registerRequest.setLoginId("test1");
registerRequest.setEmail("2827523200@qq.com");
registerRequest.setPassword("hello1234");
ResultT<String> resultT = userService.register2(registerRequest);
System.out.println(resultT);
System.out.println(resultT.getData());
}

执行结果如下:


流程引擎实现(一)—串行流程实现
https://cxydhi.github.io/2024/10/04/流程引擎实现(一)—串行流程实现/
作者
沉河不浮
发布于
2024年10月4日
许可协议