
在开发业务系统时,我们时常会遇到复杂的业务处理流程。如果单纯使用 if-else 来实现,代码不仅冗长,后期维护和迭代也相当困难。有没有一种优雅的方式来编排这些业务组件呢?今天介绍一个轻量级但功能强大的国产流程引擎框架——LiteFlow。本文将以电商业务中典型的订单价格计算场景为例,手把手带你体验如何用它来清晰、灵活地组织业务逻辑。
LiteFlow简介
LiteFlow是一个专为组件化业务编排设计的轻量级流程引擎。它的核心思想是将业务逻辑拆分为独立的组件,然后通过一份简洁易懂的规则文件将这些组件串联起来,从而构建出完整的业务流。这非常适合处理步骤繁多、带有条件分支的复杂业务场景。
LiteFlow拥有以下主要特性:
- 组件定义统一:所有业务逻辑都被封装为组件,使用Spring原生的
@Component 注解即可定义。
- 规则轻量易学:基于规则文件进行流程编排,其表达式语言简单直观,上手仅需几分钟。
- 规则格式多样:支持XML、JSON、YML三种格式的规则文件,可按团队喜好选择。
- 编排能力强大:支持串行、并行、选择、条件分支等多种编排模式,并能混合编排同步与异步组件。
- 热刷新支持:规则文件修改后无需重启应用,可实现实时生效。
- 兼容性良好:完美支持SpringBoot及非Spring环境的Java项目。
下图展示了使用LiteFlow实现的订单价格计算引擎的前端界面,计算过程涉及会员折扣、多种促销、运费等十多个步骤,逻辑清晰,结果一目了然。

强大的IDEA插件:LiteFlowX
为了提升开发体验,LiteFlow提供了专属的IDEA插件——LiteFlowX。它极大地增强了规则文件编写的便捷性,强烈推荐安装使用。
- 安装插件:在IDEA的插件市场(Plugins Marketplace)中搜索“LiteFlowX”并安装。

- 图标识别:安装后,项目中定义的LiteFlow组件和规则文件会显示特定的图标,便于快速识别。

- 智能提示与跳转:编辑规则文件时,插件会提供已定义组件的代码补全提示,并支持在组件类与规则文件之间快速跳转。

- 工具箱:插件右侧提供了一个工具箱,可以便捷地查看项目中所有的组件和流程链(Chain)。

规则表达式快速入门
LiteFlow的核心在于其规则表达式,它们被定义在规则文件中。掌握下面几种基本的编排模式,你就能应对大多数业务场景。
串行编排 (THEN)
依次执行组件a, b, c, d。
<chain name="chain1">
THEN(a, b, c, d);
</chain>
并行编排 (WHEN)
并行执行组件a, b, c。
<chain name="chain1">
WHEN(a, b, c);
</chain>
选择编排 (SWITCH)
实现类似switch的逻辑,根据组件a的返回结果,决定执行b, c, d中的哪一个。
<chain name="chain1">
SWITCH(a).to(b, c, d);
</chain>
条件编排 (IF/ELIF/ELSE)
实现if-elseif-else的条件分支逻辑。
<!-- if -->
<chain name="chain1">
IF(x, a);
</chain>
<!-- 三元运算符 if-else -->
<chain name="chain1">
IF(x, a, b);
</chain>
<!-- if-else (等价于上一句) -->
<chain name="chain1">
IF(x, a).ELSE(b);
</chain>
<!-- if-elseif-else -->
<chain name="chain1">
IF(x1, a).ELIF(x2, b).ELSE(c);
</chain>
使用子流程
对于复杂流程,可以定义子流程(子链),然后在主流程中引用,使结构更清晰。
<!-- 定义子流程 -->
<chain name="subChain">
THEN(C, D);
</chain>
<!-- 主流程中引用 -->
<chain name="mainChain">
THEN(
A, B,
subChain, <!-- 引用子流程 -->
E
);
</chain>
实战:订单价格计算引擎
了解了基本语法后,我们通过一个实际的订单价格计算案例,来看看LiteFlow如何落地。该案例模拟了电商中计算订单最终价格的完整流程,涉及参数检查、优惠转换、折扣计算、运费判断等十多个步骤。
1. 项目集成
在SpringBoot项目中,只需添加以下依赖:
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>2.8.5</version>
</dependency>
在 application.yml 中配置规则文件路径:
server:
port: 8580
liteflow:
#规则文件路径
rule-source: liteflow/*.el.xml
2. 案例结构与流程
我们将使用LiteFlow官方提供的示例项目 liteflow-example(项目地址见文末)。运行后访问 http://localhost:8580 即可体验。
其核心业务流程如下图所示,清晰地展示了从订单数据输入到最终价格输出的完整组件化流程:

3. 定义业务组件
每个计算步骤都被抽象为一个独立的组件。例如,优惠券计算组件:
/**
* 优惠券抵扣计算组件
*/
@Component("couponCmp")
public class CouponCmp extends NodeComponent {
@Override
public void process() throws Exception {
PriceContext context = this.getContextBean(PriceContext.class);
// Mock优惠券面值
Long couponId = context.getCouponId();
BigDecimal couponPrice = new BigDecimal(15);
// 计算逻辑...
BigDecimal prePrice = context.getLastestPriceStep().getCurrPrice();
BigDecimal currPrice = prePrice.subtract(couponPrice);
// 将步骤结果存入上下文
context.addPriceStep(new PriceStepVO(PriceTypeEnum.COUPON_DISCOUNT,
couponId.toString(),
prePrice,
currPrice.subtract(prePrice),
currPrice,
PriceTypeEnum.COUPON_DISCOUNT.getName()));
}
@Override
public boolean isAccess() {
// 根据业务条件决定是否执行此组件:有优惠券ID时才执行
PriceContext context = this.getContextBean(PriceContext.class);
return context.getCouponId() != null;
}
}
对于需要条件判断的节点,如判断使用国内还是境外运费规则,需使用条件组件:
/**
* 运费条件组件
*/
@Component("postageCondCmp")
public class PostageCondCmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
PriceContext context = this.getContextBean(PriceContext.class);
boolean oversea = context.isOversea();
// 根据条件返回下一个要执行的组件ID
if(oversea){
return "overseaPostageCmp";
}else{
return "postageCmp";
}
}
}
4. 编排业务流程
定义好所有组件后,使用规则文件将它们优雅地串联起来。
首先是促销计算子流程 (promotionChain):
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="promotionChain">
THEN(fullCutCmp, fullDiscountCmp, rushBuyCmp);
</chain>
</flow>
然后是主流程 (mainChain),它引用了子流程,并包含了条件分支:
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="mainChain">
THEN(
checkCmp, slotInitCmp, priceStepInitCmp,
promotionConvertCmp, memberDiscountCmp,
promotionChain, couponCmp, <!-- 引用子流程 -->
SWITCH(postageCondCmp).to(postageCmp, overseaPostageCmp), <!-- 条件分支 -->
priceResultCmp, stepPrintCmp
);
</chain>
</flow>
5. 执行流程与上下文传递
在Controller中,注入 FlowExecutor 来执行定义好的流程链。
@Controller
public class PriceExampleController {
@Resource
private FlowExecutor flowExecutor;
@RequestMapping(value = "/submit", method = RequestMethod.POST)
@ResponseBody
public String submit(@Nullable @RequestBody String reqData) {
try {
PriceCalcReqVO req = JSON.parseObject(reqData, PriceCalcReqVO.class);
// 执行名为"mainChain"的流程,并传入请求参数和上下文类
LiteflowResponse response = flowExecutor.execute2Resp("mainChain", req, PriceContext.class);
return response.getContextBean(PriceContext.class).getPrintLog();
} catch (Throwable t) {
t.printStackTrace();
return "error";
}
}
}
组件间如何共享数据? LiteFlow通过上下文(Context) 机制解决。所有组件都从同一个上下文对象(本例为 PriceContext)中获取输入数据和存储处理结果。例如,初始化组件将请求参数放入上下文:
@Component("slotInitCmp")
public class SlotInitCmp extends NodeComponent {
@Override
public void process() throws Exception {
PriceCalcReqVO req = this.getRequestData(); // 获取请求数据
PriceContext context = this.getContextBean(PriceContext.class);
context.setOrderNo(req.getOrderNo());
context.setOversea(req.isOversea());
// ... 设置其他参数
}
}
总结
通过上述案例可以看出,LiteFlow 将一个复杂的、多步骤且带有分支的订单价格计算逻辑,清晰地分解为多个独立的组件,并通过声明式的规则文件进行编排。这种方式带来的好处非常明显:
- 代码可读性高:业务流程图几乎可以直接翻译为规则文件,逻辑一目了然。
- 维护成本低:组件职责单一,修改或替换某个步骤不影响其他部分。
- 灵活性好:通过修改规则文件即可调整业务流程,无需改动代码,且支持热更新。
- 复用性强:定义好的组件可以在不同的流程链中复用。
对于需要处理复杂业务逻辑、追求代码整洁和可维护性的 Java 及 SpringBoot 项目来说,LiteFlow是一个非常值得尝试的轻量级解决方案。它学习曲线平缓,却能显著提升后端业务代码的组织能力。
项目地址
LiteFlow 开源项目地址:https://gitee.com/dromara/liteFlow
本文演示案例地址:https://gitee.com/bryan31/liteflow-example