SpringBoot?LiteFlow引擎框架使用原理解析
一、LiteFlow 簡介
LiteFlow是一個(gè)輕量且強(qiáng)大的國產(chǎn)規(guī)則引擎框架,可用于復(fù)雜的組件化業(yè)務(wù)的編排領(lǐng)域。幫助系統(tǒng)變得更加絲滑且靈活。利用LiteFlow,你可以將瀑布流式的代碼,轉(zhuǎn)變成以組件為核心概念的代碼結(jié)構(gòu),這種結(jié)構(gòu)的好處是可以任意編排,組件與組件之間是解耦的,組件可以用腳本來定義,組件之間的流轉(zhuǎn)全靠規(guī)則來驅(qū)動(dòng)。LiteFlow擁有開源規(guī)則引擎最為簡單的DSL語法。 LiteFlow官網(wǎng)
LiteFlow于2020年正式開源,2021年獲得開源中國年度最受歡迎開源軟件殊榮。于2022年獲得Gitee最有價(jià)值開源項(xiàng)目GVP榮譽(yù)。是一個(gè)正處在高速發(fā)展中的開源項(xiàng)目。LiteFlow是一個(gè)由社區(qū)驅(qū)動(dòng)的項(xiàng)目,擁有一個(gè)2500多人的使用者社區(qū)。雖然相比Acitiviti、Flowable來說,LiteFlow的知名度要低得多,功能也沒有這些知名成熟引擎那么強(qiáng)大,但LiteFlow還是有諸多優(yōu)點(diǎn),能夠滿足你絕大部分的場(chǎng)景。這些優(yōu)點(diǎn)包括:
【1】規(guī)則多樣化: 規(guī)則支持xml、json、yml三種規(guī)則文件寫法方式。
【2】使用便捷: 引幾個(gè)jar包、實(shí)現(xiàn)幾個(gè)接口、寫一個(gè)流程編排文件,就能運(yùn)行。
【3】編排豐富: 支持串行、并行、選擇、循環(huán)、異常處理、嵌套等各種編排方式。
【4】事件監(jiān)聽: 支持事件觸發(fā)和狀態(tài)變化監(jiān)聽,可以方便地?cái)U(kuò)展和定制工作流處理邏輯。
【5】異步超時(shí): 支持異步執(zhí)行和超時(shí)控制,可以提高系統(tǒng)的并發(fā)處理能力和穩(wěn)定性。
【6】支持腳本: 支持各種主流腳本語言。
【7】配置源豐富: 支持將流程定義放到ZK/DB/Etcd/Nacos/Redis/Apollo和自定義擴(kuò)展等。相當(dāng)于可以實(shí)現(xiàn)動(dòng)態(tài)配置更改。
【8】定制化: 高度可定制化,用戶可以根據(jù)自己的需求自由擴(kuò)展和定制LiteFlow的各種組件和功能。
【9】支持眾多腳本語言: LiteFlow的腳本組件,支持眾多腳本語言Groovy/JavaScript/QLExpress/Python/Lua/Aviator/Java,完全和Java打通,你可以用腳本來實(shí)現(xiàn)任何邏輯。
【10】優(yōu)雅熱刷新機(jī)制: 規(guī)則變化,無需重啟您的應(yīng)用,即時(shí)改變應(yīng)用的規(guī)則。高并發(fā)下不會(huì)因?yàn)樗⑿乱?guī)則導(dǎo)致正在執(zhí)行的規(guī)則有任何錯(cuò)亂。
【11】支持廣泛: 不管你的項(xiàng)目是不是基于Springboot,Spring還是任何其他java框架構(gòu)建,LiteFlow都能游刃有余。
【12】上下文隔離機(jī)制: 可靠的上下文隔離機(jī)制,你無需擔(dān)心高并發(fā)情況下的數(shù)據(jù)串流。
【13】性能卓越: 框架本身幾乎不消耗額外性能,性能取決你的組件執(zhí)行效率。
【14】自帶簡單監(jiān)控: 框架內(nèi)自帶一個(gè)命令行的監(jiān)控,能夠知道每個(gè)組件的運(yùn)行耗時(shí)排行。
適合使用的這項(xiàng)技術(shù)的系統(tǒng)
在每個(gè)公司的系統(tǒng)中,總有一些擁有復(fù)雜業(yè)務(wù)邏輯的系統(tǒng),這些系統(tǒng)承載著核心業(yè)務(wù)邏輯,幾乎每個(gè)需求都和這些核心業(yè)務(wù)有關(guān),這些核心業(yè)務(wù)業(yè)務(wù)邏輯冗長,涉及內(nèi)部邏輯運(yùn)算,緩存操作,持久化操作,外部資源調(diào)取,內(nèi)部其他系統(tǒng)RPC調(diào)用等等。時(shí)間一長,項(xiàng)目幾經(jīng)易手,維護(hù)成本就會(huì)越來越高。各種硬代碼判斷,分支條件越來越多。代碼的抽象,復(fù)用率也越來越低,各個(gè)模塊之間的耦合度很高。一小段邏輯的變動(dòng),會(huì)影響到其他模塊,需要進(jìn)行完整回歸測(cè)試來驗(yàn)證。如要靈活改變業(yè)務(wù)流程的順序,則要進(jìn)行代碼大改動(dòng)進(jìn)行抽象,重新寫方法。實(shí)時(shí)熱變更業(yè)務(wù)流程,幾乎很難實(shí)現(xiàn)。
如何打破僵局?LiteFlow為解耦邏輯而生,為編排而生,在使用LiteFlow之后,你會(huì)發(fā)現(xiàn)打造一個(gè)低耦合,靈活的系統(tǒng)會(huì)變得易如反掌!
二、LiteFlow 原理
如果你要對(duì)復(fù)雜業(yè)務(wù)邏輯進(jìn)行新寫或者重構(gòu),用LiteFlow最合適不過。它是一個(gè)編排式的規(guī)則引擎框架,組件編排,幫助解耦業(yè)務(wù)代碼,讓每一個(gè)業(yè)務(wù)片段都是一個(gè)組件。
LiteFlow的核心是“流程即代碼”,即將業(yè)務(wù)流程和代碼結(jié)構(gòu)緊密耦合在一起。LiteFlow采用基于XML文件的流程定義方式,通過定義流程節(jié)點(diǎn)和連線來描述整個(gè)工作流程。每個(gè)流程節(jié)點(diǎn)都對(duì)應(yīng)著Java代碼中的一個(gè)方法,而連線則對(duì)應(yīng)著方法之間的調(diào)用關(guān)系。這樣一來,我們就可以非常直觀地看到整個(gè)業(yè)務(wù)流程的處理過程,而且在修改流程時(shí)也更加方便快捷。

組件可實(shí)時(shí)熱更替,也可以給編排好的邏輯流里實(shí)時(shí)增加一個(gè)組件,從而改變你的業(yè)務(wù)邏輯。

編排語法強(qiáng)大到可以編排出任何你想要的邏輯流程例如:

三、使用場(chǎng)景
LiteFlow適用于哪些場(chǎng)景: LiteFlow適用于擁有復(fù)雜邏輯的業(yè)務(wù),比如說價(jià)格引擎,下單流程等,這些業(yè)務(wù)往往都擁有很多步驟,這些步驟完全可以按照業(yè)務(wù)粒度拆分成一個(gè)個(gè)獨(dú)立的組件,進(jìn)行裝配復(fù)用變更。使用LiteFlow,你會(huì)得到一個(gè)靈活度高,擴(kuò)展性很強(qiáng)的系統(tǒng)。因?yàn)榻M件之間相互獨(dú)立,也可以避免改一處而動(dòng)全身的這樣的風(fēng)險(xiǎn)。
LiteFlow不適用于哪些場(chǎng)景: LiteFlow不適合角色任務(wù)之間的流轉(zhuǎn),類似于審批流,A審批完應(yīng)該是B審批,然后再流轉(zhuǎn)到C角色。這里申明下,LiteFlow只做基于邏輯的流轉(zhuǎn),而不做基于角色任務(wù)的流轉(zhuǎn)。如果你想做基于角色任務(wù)的流轉(zhuǎn),推薦使用flowable,activiti這2個(gè)框架。
四、JDK支持情況
LiteFlow要求的最低的JDK版本為8,支持JDK8~JDK17所有的版本。如果你使用JDK11以上,確保LiteFlow的版本為v2.10.6及其以上版本。因?yàn)?code>LiteFlow從v2.10.6開始,對(duì)JDK11和JDK17進(jìn)行了詳細(xì)的用例測(cè)試,通過了全部的900多個(gè)測(cè)試用例。而在v2.10.6以下版本,在JDK11以上是未經(jīng)過測(cè)試用例保障的。特別需要注意的是,如果你使用JDK11及其以上的版本,請(qǐng)確保jvm參數(shù)加上以下參數(shù):
--add-opens java.base/sun.reflect.annotation=ALL-UNNAMED
五、Springboot 整合流程
LiteFlow要求的Springboot的最低的版本是2.0。支持的范圍是Springboot 2.X ~ Springboot 3.X。如果你使用了最新的Springboot 3.X,相應(yīng)的JDK版本也要切換為JDK17。
LiteFlow提供了liteflow-spring-boot-starter依賴包,提供自動(dòng)裝配功能
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>2.11.3</version>
</dependency>組件定義
在依賴了以上jar包后,你需要定義并實(shí)現(xiàn)一些組件,確保SpringBoot會(huì)掃描到這些組件并注冊(cè)進(jìn)上下文。
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
//do your business
}
}以此類推再分別定義b,c組件:
@Component("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
//do your business
}
}
@Component("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
//do your business
}
}SpringBoot配置文件
然后,在你的SpringBoot的application.properties或者application.yml里添加配置(這里以yaml為例,properties也是一樣的)
liteflow:
#規(guī)則文件路徑
rule-source: config/flow.el.xml
#-----------------以下非必須-----------------
#liteflow是否開啟,默認(rèn)為true
enable: true
#liteflow的banner打印是否開啟,默認(rèn)為true
print-banner: true
#zkNode的節(jié)點(diǎn),只有使用zk作為配置源的時(shí)候才起作用,默認(rèn)為/lite-flow/flow
zk-node: /lite-flow/flow
#上下文的最大數(shù)量槽,默認(rèn)值為1024
slot-size: 1024
#FlowExecutor的execute2Future的線程數(shù),默認(rèn)為64
main-executor-works: 64
#FlowExecutor的execute2Future的自定義線程池Builder,LiteFlow提供了默認(rèn)的Builder
main-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultMainExecutorBuilder
#自定義請(qǐng)求ID的生成類,LiteFlow提供了默認(rèn)的生成類
request-id-generator-class: com.yomahub.liteflow.flow.id.DefaultRequestIdGenerator
#并行節(jié)點(diǎn)的線程池Builder,LiteFlow提供了默認(rèn)的Builder
thread-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultWhenExecutorBuilder
#異步線程最長的等待時(shí)間(只用于when),默認(rèn)值為15000
when-max-wait-time: 15000
#異步線程最長的等待時(shí)間(只用于when),默認(rèn)值為MILLISECONDS,毫秒
when-max-wait-time-unit: MILLISECONDS
#when節(jié)點(diǎn)全局異步線程池最大線程數(shù),默認(rèn)為16
when-max-workers: 16
#并行循環(huán)子項(xiàng)線程池最大線程數(shù),默認(rèn)為16
parallelLoop-max-workers: 16
#并行循環(huán)子項(xiàng)線程池等待隊(duì)列數(shù),默認(rèn)為512
parallelLoop-queue-limit: 512
#并行循環(huán)子項(xiàng)的線程池Builder,LiteFlow提供了默認(rèn)的Builder
parallelLoop-executor-class: com.yomahub.liteflow.thread.LiteFlowDefaultParallelLoopExecutorBuilder
#when節(jié)點(diǎn)全局異步線程池等待隊(duì)列數(shù),默認(rèn)為512
when-queue-limit: 512
#是否在啟動(dòng)的時(shí)候就解析規(guī)則,默認(rèn)為true
parse-on-start: true
#全局重試次數(shù),默認(rèn)為0
retry-count: 0
#是否支持不同類型的加載方式混用,默認(rèn)為false
support-multiple-type: false
#全局默認(rèn)節(jié)點(diǎn)執(zhí)行器
node-executor-class: com.yomahub.liteflow.flow.executor.DefaultNodeExecutor
#是否打印執(zhí)行中過程中的日志,默認(rèn)為true
print-execution-log: true
#是否開啟本地文件監(jiān)聽,默認(rèn)為false
enable-monitor-file: false
#簡易監(jiān)控配置選項(xiàng)
monitor:
#監(jiān)控是否開啟,默認(rèn)不開啟
enable-log: false
#監(jiān)控隊(duì)列存儲(chǔ)大小,默認(rèn)值為200
queue-limit: 200
#監(jiān)控一開始延遲多少執(zhí)行,默認(rèn)值為300000毫秒,也就是5分鐘
delay: 300000
#監(jiān)控日志打印每過多少時(shí)間執(zhí)行一次,默認(rèn)值為300000毫秒,也就是5分鐘
period: 300000規(guī)則文件的定義
同時(shí),你得在resources下的config/flow.el.xml中定義規(guī)則:SpringBoot在啟動(dòng)時(shí)會(huì)自動(dòng)裝載規(guī)則文件。
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN(a, b, c);
</chain>
</flow>執(zhí)行
聲明啟動(dòng)類:
@SpringBootApplication
//把你定義的組件掃入Spring上下文中
@ComponentScan({"com.xxx.xxx.cmp"})
public class LiteflowExampleApplication {
public static void main(String[] args) {
SpringApplication.run(LiteflowExampleApplication.class, args);
}
}然后你就可以在Springboot任意被Spring托管的類中拿到flowExecutor,進(jìn)行執(zhí)行鏈路:這個(gè)DefaultContext是默認(rèn)的上下文,用戶可以用最自己的任意Bean當(dāng)做上下文傳入,如果需要傳入自己的上下文,則需要傳用戶Bean的Class屬性
@Component
public class YourClass{
@Resource
private FlowExecutor flowExecutor;
public void testConfig(){
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
}
}六、數(shù)據(jù)上下文
在執(zhí)行器執(zhí)行流程時(shí)會(huì)分配數(shù)據(jù)上下文實(shí)例給這個(gè)請(qǐng)求。不同請(qǐng)求的數(shù)據(jù)上下文實(shí)例是完全隔離的。里面存放著此請(qǐng)求所有的用戶數(shù)據(jù)。不同的組件之間是不傳遞參數(shù)的,所有的數(shù)據(jù)交互都是通過這個(gè)數(shù)據(jù)上下文來實(shí)現(xiàn)的。數(shù)據(jù)上下文這個(gè)概念在LiteFlow框架中非常重要,你所有的業(yè)務(wù)數(shù)據(jù)都是放在數(shù)據(jù)上下文中。要做到可編排,一定是消除每個(gè)組件差異性的。如果每個(gè)組件出參入?yún)⒍疾灰恢拢蔷蜎]法編排了。
LiteFlow對(duì)此有獨(dú)特的設(shè)計(jì)理念,平時(shí)我們寫瀑布流的程序時(shí),A調(diào)用B,那A一定要把B所需要的參數(shù)傳遞給B,而在LiteFlow框架體系中,每個(gè)組件的定義中是不需要接受參數(shù)的,也無任何返回的。每個(gè)組件只需要從數(shù)據(jù)上下文中獲取自己關(guān)心的數(shù)據(jù)即可,而不用關(guān)心此數(shù)據(jù)是由誰提供的,同樣的,每個(gè)組件也只要把自己執(zhí)行所產(chǎn)生的結(jié)果數(shù)據(jù)放到數(shù)據(jù)上下文中即可,也不用關(guān)心此數(shù)據(jù)到底是提供給誰用的。這樣一來,就從數(shù)據(jù)層面一定程度的解耦了。從而達(dá)到可編排的目的。關(guān)于這個(gè)理念,也在LiteFlow簡介中的設(shè)計(jì)原則有提到過,給了一個(gè)形象的例子,大家可以再去看看。
一旦在數(shù)據(jù)上下文中放入數(shù)據(jù),整個(gè)鏈路中的任一節(jié)點(diǎn)都是可以取到的。
默認(rèn)上下文
LiteFlow提供了一個(gè)默認(rèn)的數(shù)據(jù)上下文的實(shí)現(xiàn):DefaultContext。這個(gè)默認(rèn)的實(shí)現(xiàn)其實(shí)里面主要存儲(chǔ)數(shù)據(jù)的容器就是一個(gè)Map。你可以通過DefaultContext中的setData方法放入數(shù)據(jù),通過getData方法獲得數(shù)據(jù)。
::: warningDefaultContext雖然可以用,但是在實(shí)際業(yè)務(wù)中,用這個(gè)會(huì)存在大量的弱類型,存取數(shù)據(jù)的時(shí)候都要進(jìn)行強(qiáng)轉(zhuǎn),頗為不方便。所以官方建議你自己去實(shí)現(xiàn)自己的數(shù)據(jù)上下文。
:::
自定義上下文
在一個(gè)流程中,總會(huì)有一些初始的參數(shù),比如訂單號(hào),用戶Id等等一些的初始參數(shù)。這時(shí)候需要通過以下方法的第二個(gè)參數(shù)傳入:
//參數(shù)為流程ID,無初始流程入?yún)?,上下文類型為默認(rèn)的DefaultContext public LiteflowResponse execute2Resp(String chainId) //第一個(gè)參數(shù)為流程ID,第二個(gè)參數(shù)為流程入?yún)?。上下文類型為默認(rèn)的DefaultContext public LiteflowResponse execute2Resp(String chainId, Object param); //第一個(gè)參數(shù)為流程ID,第二個(gè)參數(shù)為流程入?yún)?,后面可以傳入多個(gè)上下文class public LiteflowResponse execute2Resp(String chainId, Object param, Class<?>... contextBeanClazzArray) //第一個(gè)參數(shù)為流程ID,第二個(gè)參數(shù)為流程入?yún)?,后面可以傳入多個(gè)上下文的Bean public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray)
你可以用你自己的任意的Bean當(dāng)做上下文進(jìn)行傳入。LiteFlow對(duì)上下文的Bean沒有任何要求。自己定義的上下文實(shí)質(zhì)上就是一個(gè)最簡單的值對(duì)象,自己定義的上下文因?yàn)槭菑?qiáng)類型,更加貼合業(yè)務(wù)。你可以像這樣進(jìn)行傳入:
LiteflowResponse response = flowExecutor.execute2Resp("chain1", 流程初始參數(shù), CustomContext.class);傳入之后,LiteFlow會(huì)在調(diào)用時(shí)進(jìn)行初始化,給這個(gè)上下文分配唯一的實(shí)例。你在組件之中可以這樣去獲得這個(gè)上下文實(shí)例:
@LiteflowComponent("yourCmpId")
public class YourCmp extends NodeComponent {
@Override
public void process() {
OrderContext orderContext = this.getContextBean(OrderContext.class);
UserContext userContext = this.getContextBean(UserContext.class);
SignContext signContext = this.getContextBean(SignContext.class);
//如果你只想獲取第一個(gè)上下文,第一個(gè)上下文是OrderContext,那么也可以用這個(gè)方法
//OrderContext orderContext = this.getFirstContextBean();
...
}
}多上下文
LiteFlow在新版本中支持了多上下文,在執(zhí)行的時(shí)候同時(shí)初始化你傳入的多個(gè)上下文。在組件里也可以根據(jù)class類型很方便的拿到。你可以像這樣進(jìn)行傳入:
LiteflowResponse response = flowExecutor.execute2Resp("chain1", 流程初始參數(shù), OrderContext.class, UserContext.class, SignContext.class);在組件之中可以這樣去獲得這個(gè)上下文實(shí)例:
@LiteflowComponent("yourCmpId")
public class YourCmp extends NodeComponent {
@Override
public void process() {
OrderContext orderContext = this.getContextBean(OrderContext.class);
UserContext userContext = this.getContextBean(UserContext.class);
SignContext signContext = this.getContextBean(SignContext.class);
//如果你只想獲取第一個(gè)上下文,第一個(gè)上下文是OrderContext,那么也可以用這個(gè)方法
//OrderContext orderContext = this.getFirstContextBean();
...
}
}用初始化好的上下文傳入
LiteFlow從2.8.4版本開始,允許用戶傳入一個(gè)或多個(gè)已經(jīng)初始化好的bean作為上下文,而不是傳入class對(duì)象。在拿到FlowExecutor之后,你可以像如下一樣,傳入已經(jīng)初始化好的bean作為上下文(當(dāng)然也支持多上下文,這里只演示單上下文):
OrderContext orderContext = new OrderContext();
orderContext.setOrderNo("SO11223344");
LiteflowResponse response = flowExecutor.execute2Resp("chain1", null, orderContext);::: warning
框架并不支持上下文bean和class混傳,你要么都傳bean,要么都傳class。
:::
七、異步Future
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Class<?>... contextBeanClazzArray)
如果調(diào)用這個(gè)方法,那就是無阻塞的,想要拿到response,請(qǐng)用得到的future.get()就可以了。同時(shí),主執(zhí)行器在這個(gè)模式下的線程數(shù)和線程池也可以自定義,具體配置如下,LiteFlow已經(jīng)設(shè)置了預(yù)設(shè)值,你也可自己定義。
liteflow.main-executor-works=64 liteflow.main-executor-class=com.yomahub.liteflow.thread.LiteFlowDefaultMainExecutorBuilder
如果你定義了自定義線程池,你需新建一個(gè)類,然后實(shí)現(xiàn)ExecutorBuilder接口:
public class CustomThreadBuilder implements ExecutorBuilder {
@Override
public ExecutorService buildExecutor() {
return Executors.newCachedThreadPool();
}
}八、規(guī)則寫法 串行編排
如果你要依次執(zhí)行a,b,c,d四個(gè)組件,你可以用THEN關(guān)鍵字,需要注意的是,THEN必須大寫。
<chain name="chain1">
THEN(a, b, c, d);
</chain>并行編排
如果你要并行執(zhí)行a,b,c三個(gè)組件,你可以用WHEN關(guān)鍵字,需要注意的是,WHEN必須大寫。
<chain name="chain1">
WHEN(a, b, c);
</chain>和串行嵌套起來:讓我們把THEN和WHEN結(jié)合起來用,看一個(gè)示例:b,c,d默認(rèn)并行都執(zhí)行完畢后,才會(huì)執(zhí)行e。
<chain name="chain1">
THEN(
a,
WHEN(b, c, d),
e
);
</chain>
上面的示例應(yīng)該很好理解吧,那么再看一個(gè)示例:
<chain name="chain1">
THEN(
a,
WHEN(b, THEN(c, d)),
e
);
</chain>
忽略錯(cuò)誤: WHEN關(guān)鍵字提供了一個(gè)子關(guān)鍵字ignoreError(默認(rèn)為`false``)來提供忽略錯(cuò)誤的特性,用法如下:
<chain name="chain1">
THEN(
a,
WHEN(b, c, d).ignoreError(true),
e
);
</chain>
設(shè)b,c,d中任一一個(gè)節(jié)點(diǎn)有異常,那么最終e仍舊會(huì)被執(zhí)行。
任一節(jié)點(diǎn)先執(zhí)行完則忽略其他: WHEN關(guān)鍵字提供了一個(gè)子關(guān)鍵字any(默認(rèn)為false)用來提供并行流程中,任一條分支先執(zhí)行完即忽略其他分支,繼續(xù)執(zhí)行的特性。用法如下:假設(shè)e節(jié)點(diǎn)先執(zhí)行完,那么不管其他分支是否執(zhí)行完,會(huì)立馬執(zhí)行節(jié)點(diǎn)f。
<chain name="chain1">
THEN(
a,
WHEN(b, THEN(c, d), e).any(true),
f
);
</chain>
指定任意節(jié)點(diǎn)先執(zhí)行完則忽略其他: LiteFlow從v2.11.1開始,支持了并行編排中指定節(jié)點(diǎn)的執(zhí)行則忽略其他,WHEN關(guān)鍵字新增子關(guān)鍵字must(不可為空),可用于指定需等待執(zhí)行的任意節(jié)點(diǎn),可以為1個(gè)或者多個(gè),若指定的所有節(jié)點(diǎn)率先完成,則繼續(xù)往下執(zhí)行,忽略同級(jí)別的其他任務(wù),用法如下:must指定了b,c,則b,c是一定會(huì)被執(zhí)行完畢了,如果b,c執(zhí)行完畢了后d還未執(zhí)行完,則忽略,直接執(zhí)行下一個(gè)組件f。
<chain name="chain1">
THEN(
a,
WHEN(b, c, d).must(b, c),
f
);
</chain>以上是單節(jié)點(diǎn)的用法,must還可以指定一個(gè)或多個(gè)表達(dá)式。比如:WHEN里有一個(gè)嵌套的THEN,如果需要指定這個(gè)表達(dá)式,則需要給這個(gè)表達(dá)式設(shè)置一個(gè)id,must里需要指定這個(gè)id,需要注意的是,must里指定id,需要用引號(hào)括起來。
<chain name="chain1">
THEN(
a,
WHEN(b, THEN(c, d).id("t1"), e).must(b, "t1"),
f
);
</chain>開啟WHEN線程池隔離:
目前liteflow設(shè)計(jì)里when線程池,如果你不單獨(dú)設(shè)置自定義線程池,那么就會(huì)用默認(rèn)的線程池。而這個(gè)線程池,是所有的when共同一個(gè)。LiteFlow從2.11.1開始,提供一個(gè)liteflow.when-thread-pool-isolate參數(shù),默認(rèn)為false,如果設(shè)為true,則會(huì)開啟WHEN的線程池隔離機(jī)制,這意味著每一個(gè)when都會(huì)有單獨(dú)的線程池。這個(gè)特性對(duì)于運(yùn)行復(fù)雜的嵌套when時(shí)是可以提升運(yùn)行速度的且規(guī)避掉一些鎖的問題。
你可以如下配置來開啟:
liteflow.when-thread-pool-isolate=true
選擇編排
我們?cè)趯憳I(yè)務(wù)邏輯的時(shí)候,通常會(huì)碰到選擇性問題,即,如果返回結(jié)果1,則進(jìn)入A流程,如果返回結(jié)果2,則進(jìn)入B流程,如果返回結(jié)果3,則進(jìn)入C流程。在有些流程定義中也被定義為排他網(wǎng)關(guān)。這個(gè)通過LiteFLow的表達(dá)式也非常容易實(shí)現(xiàn),你可以用SWITCH...TO的組合關(guān)鍵字,注意的是SWITCH必須大寫,to大小寫均可。
如果,根據(jù)組件a,來選擇執(zhí)行b,c,d中的一個(gè),你可以如下聲明:

@LiteflowComponent("a")
public class ACmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
System.out.println("Acomp executed!");
return "c";
}
}DEFAULT關(guān)鍵字: LiteFlow從2.9.5開始,對(duì)選擇編排新增了一個(gè)DEFAULT關(guān)鍵字。用法為SWITCH...TO...DEFAULT。比如如下表達(dá)式:
<chain name="chain1">
SWITCH(x).TO(a, b, c).DEFAULT(y);
</chain>如上表達(dá)式的x如果返回非a,b,c中的一個(gè),則默認(rèn)選擇到y。當(dāng)然DEFAULT里面也可以是一個(gè)表達(dá)式。
選擇編排中的id語法: 接下來展示一個(gè)SWITCH中套THEN和WHEN的例子。如果你閱讀過選擇組件這一章,就應(yīng)該知道,LiteFlow通過選擇組件的返回來確定該選擇什么。那么如果SWITCH中套一個(gè)THEN,那么選擇組件如果要選擇這個(gè)THEN應(yīng)該返回什么呢?LiteFlow中規(guī)定,每個(gè)表達(dá)式都可以有一個(gè)id值,你可以設(shè)置id值來設(shè)置一個(gè)表達(dá)式的id值。然后在選擇組件里返回這個(gè)id即可。用法如下:
<chain name="chain1">
THEN(
a,
SWITCH(b).to(
c,
THEN(d, e).id("t1")
),
f
);
</chain>
如果你想選擇THEN這個(gè)表達(dá)式,那么你可以在選擇節(jié)點(diǎn)里返回t1:
@LiteflowComponent("b")
public class BCmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
//do your biz
return "t1";
}
}選擇編排中的tag語法: 事實(shí)上,除了給表達(dá)式賦值id屬性之外,你還可以給表達(dá)式賦值tag屬性。用法如下:
<chain name="chain1">
THEN(
a,
SWITCH(b).to(
c,
THEN(d, e).tag("t1")
),
f
);
</chain>如果你想選擇THEN這個(gè)表達(dá)式,那么你可以在選擇節(jié)點(diǎn)里返回:
@LiteflowComponent("b")
public class BCmp extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
return "tag:t1";
//以下這種也是可以的
return ":t1";
}
}條件編排
條件編排是選擇編排一個(gè)變種,選擇編排是根據(jù)邏輯去選擇多個(gè)子項(xiàng)中的一項(xiàng)。而條件編排只有真和假2個(gè)子項(xiàng),這處理某些業(yè)務(wù)的過程中非常有用。其實(shí)簡單來說,條件編排就是編程語言中的if else。只不過在LiteFlow EL語法中有一些不一樣的用法。以下IF和ELIF的第一個(gè)參數(shù)要求定義條件組件。
IF的二元表達(dá)式: 其中x為條件節(jié)點(diǎn),為真的情況下,執(zhí)行鏈路就為x->a->b,為假鏈路就為x->b。
<chain name="chain1">
THEN(
IF(x, a),
b
);
</chain>
@Component("x")
public class XCmp extends NodeIfComponent {
@Override
public boolean processIf() throws Exception {
//do your biz
return true;
}
}IF的三元表達(dá)式: 其中x為條件節(jié)點(diǎn),為真的情況下,執(zhí)行鏈路就為x->a->c,為假鏈路就為x->b->c。
<chain name="chain1">
THEN(
IF(x, a, b),
c
);
</chain>
ELIF表達(dá)式: ELIF關(guān)鍵字的用法其實(shí)和java語言的else if類似,可以跟多個(gè),和IF二元表達(dá)式參數(shù)一樣,一般最后還會(huì)跟個(gè)ELSE,用于多重條件的判斷:
<chain name="chain1">
IF(x1, a).ELIF(x2, b).ELIF(x3, c).ELIF(x4, d).ELSE(THEN(m, n));
</chain>
循環(huán)編排
FOR循環(huán): FOR循環(huán)表達(dá)式用于固定次數(shù)的循環(huán),通常的用法為:
<chain name="chain1">
FOR(5).DO(THEN(a, b));
</chain>上述表達(dá)式表示把a->b這個(gè)鏈路固定循環(huán)了5次。如果你在定義規(guī)則的時(shí)候并不確定要循環(huán)幾次,要在代碼運(yùn)行的時(shí)候才知道。那你也可以這樣定義:
<chain name="chain1">
FOR(f).DO(THEN(a, b));
</chain>其中f這個(gè)節(jié)點(diǎn)需要為次數(shù)循環(huán)組件,返回一個(gè)int循環(huán)次數(shù),f節(jié)點(diǎn)的定義,需要繼承NodeForComponent,需要實(shí)現(xiàn)processFor方法:
@LiteflowComponent("f")
public class FCmp extends NodeForComponent {
@Override
public int processFor() throws Exception {
//這里根據(jù)業(yè)務(wù)去返回for的結(jié)果
}
}循環(huán)下標(biāo)獲?。宏P(guān)鍵字
FOR...DO...中DO里面的任意java組件都可以通過this.getLoopIndex()來獲得下標(biāo)。在腳本中通過_meta.loopIndex來獲取。
WHILE循環(huán):
<chain name="chain1">
WHILE(w).DO(THEN(a, b));
</chain>其中w這個(gè)節(jié)點(diǎn)需要為條件循環(huán)組件,返回一個(gè)布爾值,為true則繼續(xù)循環(huán)
@LiteflowComponent("w")
public class WCmp extends NodeWhileComponent {
@Override
public boolean processWhile() throws Exception {
//這里根據(jù)業(yè)務(wù)去返回while的結(jié)果
}
}循環(huán)下標(biāo)獲取:關(guān)鍵字
WHILE...DO...中DO里面的任意節(jié)點(diǎn)都可以通過this.getLoopIndex()來獲得下標(biāo)。在腳本中通過_meta.loopIndex來獲取。
ITERATOR迭代循環(huán)
<chain name="chain1">
ITERATOR(x).DO(THEN(a, b));
</chain>其中x這個(gè)節(jié)點(diǎn)需要為迭代循環(huán)組件,返回一個(gè)迭代器:x節(jié)點(diǎn)的定義,需要繼承NodeIteratorComponent,需要實(shí)現(xiàn)processIterator方法:
@LiteflowComponent("x")
public class XCmp extends NodeIteratorComponent {
@Override
public Iterator<?> processIterator() throws Exception {
List<String> list = ListUtil.toList("jack", "mary", "tom");
return list.iterator();
}
}BREAK
LiteFlow同樣也支持BREAK語法,代表退出循環(huán)。BREAK關(guān)鍵字可以跟在FOR和WHILE后面,通常用法為:
<chain name="chain1">
FOR(f).DO(THEN(a, b)).BREAK(c);
</chain>
<chain name="chain1">
WHILE(w).DO(THEN(a, b)).BREAK(c);
</chain>其中c這個(gè)節(jié)點(diǎn)需要為退出循環(huán)組件,返回一個(gè)布爾值,為true則退出循環(huán)。c節(jié)點(diǎn)的定義,需要繼承NodeBreakComponent,需要實(shí)現(xiàn)processBreak方法:
@LiteflowComponent("c")
public class CCmp extends NodeBreakComponent {
@Override
public boolean processBreak() throws Exception {
//這里根據(jù)業(yè)務(wù)去返回break的結(jié)果
}
}BREAK關(guān)鍵字是在每次循環(huán)的末尾進(jìn)行判斷的。
到此這篇關(guān)于SpringBoot——LiteFlow引擎框架的文章就介紹到這了,更多相關(guān)SpringBoot LiteFlow引擎內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
elasticsearch 8.2.3 安裝及springboot簡單使用
這篇文章主要介紹了elasticsearch 8.2.3 安裝及springboot簡單使用,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06
關(guān)于Idea中的.properties文件顯示問題
這篇文章主要介紹了關(guān)于Idea中的.properties文件顯示問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Spring Boot 與 Kotlin 使用JdbcTemplate連接MySQL數(shù)據(jù)庫的方法
本文介紹在Spring Boot基礎(chǔ)下配置數(shù)據(jù)源和通過 JdbcTemplate 編寫數(shù)據(jù)訪問的示例。感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-01-01
Java基本知識(shí)點(diǎn)之變量和數(shù)據(jù)類型
這篇文章主要給大家介紹了關(guān)于Java基本知識(shí)點(diǎn)之變量和數(shù)據(jù)類型的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
詳解將Eclipse代碼導(dǎo)入到AndroidStudio的兩種方式
本篇文章主要介紹了詳解將Eclipse代碼導(dǎo)入到AndroidStudio的兩種方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12
Java日期時(shí)間處理問題(從Date、Calendar到SimpleDateFormat)
這篇文章主要介紹了Java日期時(shí)間處理深度解析(從Date、Calendar到SimpleDateFormat),我們?cè)敿?xì)討論了Java中的日期和時(shí)間處理,包括Date、Calendar和SimpleDateFormat類的使用,以及Java?8引入的新的日期時(shí)間API的優(yōu)勢(shì),需要的朋友可以參考下2024-08-08
java并發(fā)編程專題(十一)----(JUC原子類)數(shù)組類型詳解
這篇文章主要介紹了JAVA JUC原子類 數(shù)組類型詳解的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07
SpringAOP實(shí)現(xiàn)登錄驗(yàn)證的操作代碼
這篇文章主要介紹了SpringAOP實(shí)現(xiàn)登錄驗(yàn)證功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01

