1. 简介
LiteFlow是一个非常强大的现代化的规则引擎框架,融合了编排特性和规则引擎的所有特性,能将复杂的业务流程通过组件化的方式解耦每个步骤、重新编排以打造一个低耦合又灵活的系统。
重点概念: 规则 、组件
利用LiteFlow,你可以将瀑布流式的代码,转变成以组件为核心概念的代码结构,这种结构的好处是可以任意编排,组件与组件之间是解耦的,组件可以用脚本来定义,组件之间的流转全靠规则来驱动。
编排规则: 组件按照一定逻辑顺序执行的约束,也可称之为 业务逻辑
复杂的编排示例:
2. 组件
组件是抽取业务流程的原子性操作的代码实现, 通常单个组件是一个类,并且根据不同类型继承LiteFlow的基础组件, 并实现基类的process方法。
组件分为:普通组件、选择组件、条件组件、循环组件
涉及规则关键字: THEN 、WHEN、SWITCH、IF...ELIF...ELSE、FOR...DO...、WHILE...DO...、ITERATOR...DO...****
This关键字 :this关键字在组件中用于获取上下文、使用 this.getContextBean(clazz) 获取整个业务流程中的所有需要的数据,更详细用法会放在文章末尾。
2.1 普通组件
普通节点需要继承基类 NodeComponent , 可用于 THEN 和 WHEN 关键字中,并实现基类的 process方法
@LiteflowComponent("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
// 业务代码
}
}
注: LiteflowComponent 注解中的 参数a , 就是规则中的组件id
2.2 选择组件
依据业务动态选择下级节点的组件、需要继承基类 NodeSwitchComponent ,可用于 SWITCH关键字中,并实现基类的 processSwitch 方法
@LiteflowComponent("a")
public class ACmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
// 业务代码
return "c";
}
}
注: LiteflowComponent 注解中的 参数 a,就是规则中的组件id, 最后return 的 c 就是下一节点的组件id
2.3 条件组件
依据业务返回true/false的组件、也成为IF组件, 需要继承 NodeIfComponent,可用于 IF...ELIF...ELSE 等关键字
@Component("x")
public class XCmp extends NodeIfComponent {
@Override
public boolean processIf() throws Exception {
//业务代码
return true;
}
}
注: Component 注解和LiteflowComponent 注解等效,只是后者增加许多方法,上面的x是规则中的组件id, 最后return 的 true会给规则、用于三元表达式中选择下一节点
2.4 循环组件
依据业务针对部分需要循环执行的组件、比如分页查询某个接口的场景、也分为次数循环、条件循环、迭代循环等组件、分别需要继承基类 。
次数循环需要继承 NodeForComponent 基类,并实现processFor方法,可用于 FOR...DO... 关键字
条件循环需要继承 NodeWhileComponent 基类, 并实现 processWhile 方法、可用于 WHILE...DO...关键字
迭代循环需要继承 NodeIteratorComponent 基类,并实现 processIterator 方法, 可用于 ITERATOR...DO... 关键字
退出循环需要继承 NodeBreakComponent 基类, 并实现 processBreak 方法, 可用于 BREAK 关键字
/*** 次数循环组件 ***/
@LiteflowComponent("f")
public class FCmp extends NodeForComponent {
@Override
public int processFor() throws Exception {
//这里根据业务去返回for的结果
}
}
/*** 条件循环组件 ***/
@LiteflowComponent("w")
public class WCmp extends NodeWhileComponent {
@Override
public boolean processWhile() throws Exception {
//这里根据业务去返回while的结果
}
}
/*** 迭代循环组件 ***/
@LiteflowComponent("x")
public class XCmp extends NodeIteratorComponent {
@Override
public Iterator<?> processIterator() throws Exception {
List<String> list = ListUtil.toList("jack", "mary", "tom");
return list.iterator();
}
}
/*** 退出循环组件 ***/
@LiteflowComponent("c")
public class CCmp extends NodeBreakComponent {
@Override
public boolean processBreak() throws Exception {
//这里根据业务去返回break的结果
}
}
3. 规则
使用EL表达式描述组件之间业务流程的内容
规则分类: 串行编排、并行编排、条件编排、循环编排等
涉及关键字:THEN、WHEN、SWITCH、IF、 F OR...DO...、WHILE...DO...、ITERATOR...DO...****
3.1 串行编排
规则内部的组件执行逻辑属于串行操作,使用关键字 THEN 引导
<chain name="chain1">
THEN(a, b, c, d);
</chain>
注: 该表达式表示的是 从组件 a 然后是b、接着c、d依次执行
3.2 并行编排
规则内部的组件执行逻辑属于并行操作、类似多线程并行,使用关键字 WHEN引导
<chain name="chain1">
THEN(
a,
WHEN(b, c, d),
e
);
</chain>
注: 该表达式表示 组件 b、c 、d同时执行、不分先后顺序
3.3 选择编排
规则内部是选择逻辑、根据业务返回选择的结果,使用 SWITCH引导
<chain name="chain1">
SWITCH(a).to(b, c, d);
</chain>
注:该表达式表示依据组件a,返回的结果判断下一节点走b、c、d哪个组件
3.4 循环编排
循环分为 次数循环、条件循环、编排循环,使用F OR...DO...、WHILE...DO...、ITERATOR...DO... ****关键字引导
/*** 次数循环 ***/
<chain name="chain1">
FOR(f).DO(THEN(a, b));
</chain>
注: 组件f返回需要循环执行的次数、然后a->b这个链路依据组件f返回的次数循环
/*** 条件循环 ***/
<chain name="chain1">
WHILE(w).DO(THEN(a, b));
</chain>
注:组件w会返回true/false,如果是true则继续下一节点a->b,否则不执行下一节点
/*** 迭代循环 ***/
<chain name="chain1">
ITERATOR(x).DO(THEN(a, b));
</chain>
注: 组件x会返回一个迭代器如 list.iterator() 然后a->b的链路执行
4. 规则文件
用于存储组件及规则内容EL表达式的文件、可以存在于数据库动态化存储、也可以以文件的形式存在项目中
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<nodes>
<node id="a" class="com.yomahub.liteflow.test.parser.cmp.ACmp"/>
<node id="b" class="com.yomahub.liteflow.test.parser.cmp.BCmp"/>
<node id="c" class="com.yomahub.liteflow.test.parser.cmp.CCmp"/>
<node id="d" class="com.yomahub.liteflow.test.parser.cmp.DCmp"/>
</nodes>
<chain name="chain1">
THEN(
a, b, WHEN(c,d)
);
</chain>
</flow>
注:node节点是组件信息、id标记的就是组件id、class是组件的类路径
chain 标记的就是编排EL表达式, name是编排规则的名称
存储项目中的位置
5. 执行器与数据上下文
执行器是一个流程的触发点、用于开始一个编排流程,可以在任意的类中注入 FlowExecutor ,然后调用其方法即可。
数据上下文是在一个流程中的组件之间是解耦的、数据上下文用于存储流程中的数据,使用This关键字可以在各组件内部获取所有该流程内流转的数据信息。
/*** 列举了 FlowExecutor 基类中定义的两个常用的执行器方法 ***/
//第一个参数为流程ID,第二个参数为流程入参,后面可以传入多个上下文class
public LiteflowResponse execute2Resp(String chainId, Object param, Class<?>... contextBeanClazzArray)
//第一个参数为流程ID,第二个参数为流程入参,后面可以传入多个上下文的Bean
public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray)
/*** 实际使用的模板 ***/
public class LiteFlowService {
@Resource
private FlowExecutor customerFlowExecutor;
/**
* 调用执行器
*
* @param executeParam 入参
*/
public String executeFlow(ExecuteParam executeParam){
String chainId = executeParam.getChainId();
log.info("入参:executeParam=[{}]", JsonUtil.toJsonString(executeParam));
LiteflowResponse chainResponse = customerFlowExecutor.execute2Resp(chainId, executeParam.getParam(), User.class, Grade.class, Order.class, OrderSubDTO.class, Map.class);
//log.info("回参:chainResponse=[{}]", JsonUtil.toJsonString(chainResponse));
if (!chainResponse.isSuccess()){
Exception cause = chainResponse.getCause();
log.info("异常信息cause=[{}]", JsonUtil.toJsonString(cause));
chainResponse.getCode();
}
Map<String, CmpStep> executeSteps = chainResponse.getExecuteSteps();
log.info("执行步骤:executeSteps=[{}]", JsonUtil.toJsonString(executeSteps));
Object responseData = chainResponse.getSlot().getResponseData();
log.info("返回前端: responseData=[{}]", JsonUtil.toJsonString(responseData));
Grade grade = chainResponse.getSlot().getContextBean(Grade.class);
User user = chainResponse.getSlot().getContextBean(User.class);
Order order = chainResponse.getSlot().getContextBean(Order.class);
log.info("最后获得grade=[{}] user=[{}], order=[{}]", CoreJsonUtil.objectToJson(grade), CoreJsonUtil.objectToJson(user), CoreJsonUtil.objectToJson(order));
return JsonUtil.toJsonString(responseData);
}
}
注:列举两个常用的执行器、这是 FlowExecutor 基类中的实现方法,我们使用时候需要注入该基类、调用方法即可
@LiteflowComponent("ageHigh")
public class AgeHighComponent extends NodeComponent {
@Override
public void process() throws Exception {
log.info("当前用户大于10岁,属于高年龄!");
// 获取到组件参数对象
User user = this.getSlot().getContextBean(User.class);
// 组装组件参数体
user = CoreReflectUtil.getReflectedClassData(user, this.getRequestData());
// 执行业务操作
if(ObjectUtil.isNotNull(user)){
user.setExecuteStep(user.getExecuteStep() + "|=====>>>>|" + "执行AgeHighComponent组件");
}
// 组件返回参数
this.getSlot().setResponseData(user);
}
}
注:组件内使用this关键字获取组件业务流程之间流转的数据