一般的時(shí)候,我們都采用編程式開發(fā),編程式開發(fā)的好處非常明顯:直接、高效、自由,當(dāng)然其缺點(diǎn)也是有的,與其優(yōu)點(diǎn)剛好相對,因?yàn)橹苯?,所以有些變化都要進(jìn)行代碼上的修改;因?yàn)楦咝?,所以一旦出問題,導(dǎo)致的結(jié)果也比較嚴(yán)重,因?yàn)樽杂?,所以帶來的修改風(fēng)險(xiǎn)也比較大。 這也就是許多大的公司都在進(jìn)行流程化開發(fā)的重要原因之一,比如:上海普元,Livebos, Justep,還有許許多多知名不知名的公司都有類似的流程化開發(fā)引擎存在,通過流程化開發(fā),增強(qiáng)代碼的復(fù)用性,降低軟件開發(fā)成本及測試成本,提升軟件的可維護(hù)性及降低維護(hù)成本。
在設(shè)計(jì)Tiny框架時(shí),我們也考慮了自己的方案,主要包括以下幾個(gè)方面的問題:
組件的擴(kuò)充的便捷性是指,流程其實(shí)玩的就是組件,如果組件擴(kuò)充起來非常困難,會直接影響到流程引擎的可用性。所以Tiny框架的流程引擎的組件結(jié)構(gòu)非常之簡單,僅有一個(gè)接口方法;流程組件的注冊與加載也是非常重要的,如果在擴(kuò)充流程組件的時(shí)候,需要復(fù)雜的注冊或配置過程,這個(gè)時(shí)候流程擴(kuò)充的便捷性也會大大降低。Tiny框架采用了引用即注冊的方案,只要把流程組件放入系統(tǒng)運(yùn)行環(huán)境之間,就完成了流程組件的注冊,即可以在流程中使用,便得流程組件的擴(kuò)充的便捷性大大提高。
流程的面向特性支持是指在Tiny框架中流程是具有面向?qū)ο蟮奶匦缘?。流程可以進(jìn)行繼承,這樣帶來一個(gè)好處就是多個(gè)流程中重復(fù)的部分,可以定義在一個(gè)父流程中,然后子流程只要繼承父流程,即可;流程節(jié)點(diǎn)是可以被覆蓋的,也就是說,在父流程中可以定義一個(gè)空節(jié)點(diǎn),但是流程中定義了流轉(zhuǎn)關(guān)系,但是流程節(jié)點(diǎn)的實(shí)現(xiàn)留在子流程中實(shí)現(xiàn);
流程的編輯必須方便、容易,有專門的流程編輯工具更好,沒有的時(shí)候,使用普通的Xml編輯器也可以方便的進(jìn)行編輯。
一般的流程引擎都是不可重入的,也就是只能從開始執(zhí)行,執(zhí)行到結(jié)束結(jié)點(diǎn)之后完成。Tiny流程引擎支持流程重入,也就是說,不一定是從開始結(jié)點(diǎn)執(zhí)行,可以從任意一個(gè)結(jié)點(diǎn)執(zhí)行。這個(gè)機(jī)制為程序的邏輯提供了非常大的自由度,可以利用此特性容易的構(gòu)建頁面流引擎或工作流引擎。即使是業(yè)務(wù)流程引擎,也會由此獲得更大的自由度。
由于支持流程的可重入性,在本流程處理當(dāng)中,不僅可以在當(dāng)前流程中進(jìn)行切換與轉(zhuǎn)接,還可以流轉(zhuǎn)到其他流程的節(jié)點(diǎn)當(dāng)中,這在業(yè)務(wù)處理及頁面處理,流程處理方面都提供了極大的使得,但是這也是一個(gè)雙刃劍,在提供了這么靈活的功能的同時(shí),也會導(dǎo)致業(yè)務(wù)流程看起來比較復(fù)雜,因此,控制方面最好由架構(gòu)師或核心開發(fā)人員來編寫,普通開發(fā)人員只開發(fā)具體的業(yè)務(wù)點(diǎn)即可。
呵呵,說了這么多,大家理解起來可能還是比較抽象,那就來個(gè)例子看看:
<flow id="1000" name="Hello">
<nodes>
<node id="begin">
<component class-name="org.tinygroup.flow.HelloWorldComponent">
<properties>
<property name="name" value="world" />
</properties>
</component>
</node>
</nodes>
< /flow>
HelloWorldComponent的源碼如下:
public class HelloWorldComponent implements ComponentInterface {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void execute(Context context) {
context.put("result", String.format("Hello, %s", name));
}
}
可以看出,所有組件必須實(shí)現(xiàn)ComponentInterface 接口 從其實(shí)現(xiàn)邏輯可以看出,它就是把“Hello, ”加上輸入的名字,放在了環(huán)境變量的result當(dāng)中。 下面看看執(zhí)行結(jié)果: a.按默認(rèn)開始結(jié)點(diǎn)開始執(zhí)行
Context context = new ContextImpl();
flowExecutor.execute("1000", context);
assertEquals("Hello, world", context.get("result"));
b.從指定節(jié)點(diǎn)開始執(zhí)行
Context context = new ContextImpl();
flowExecutor.execute("1000","begin", context);
assertEquals("Hello, world", context.get("result"));
可以看到確實(shí)是執(zhí)行并返回了結(jié)果,但是它的執(zhí)行機(jī)理是怎么樣的呢?? 實(shí)際上,上面的流程是一個(gè)簡化的流程,就是說Tiny流程引擎的有些參數(shù)不輸入,也可以按照約定正確的執(zhí)行,實(shí)際上寫得完整的話,例子是下面這個(gè)樣子的:
<flow id="1000" version="1.0" privateContext="false" extend-flow-id="" name="Hello" title="你好示例" default-node-id="end" begin-node-id="begin" end-node-id="end" enable="true">
<description>some thing....</description>
<nodes>
<node id="begin">
<component class-name="org.tinygroup.flow.HelloWorldComponent">
<properties>
<property name="name" value="world"/> <span></span> </properties>
</component>
<next-nodes>
<next-node exception-type="java.lang.Exception" next-node-id="end"/>
</next-nodes>
</node>
</nodes>
< /flow>
其中flow節(jié)點(diǎn)的屬性含義為:
如果不指定,則begin-node-id默認(rèn)為begin,end-node-id默認(rèn)為end
上面說到繼承,流程繼承實(shí)現(xiàn)起來是非常簡單的,只要在extend-flow-id屬性中指定即可。
繼承不支持多繼承,即流程只能繼承自一個(gè)流程,但是可以支持多層繼承,即
a>b>c>d.....
實(shí)際開發(fā)過程中,不要把繼承搞得太復(fù)雜,這樣會把程序邏輯搞得更難理解的。
繼承實(shí)際會起到什么作用呢?
首先,會繼承一些屬性,另外會把節(jié)點(diǎn)信息繼承過來。 簡單來說就是:兩者都有,當(dāng)前流程說了算,當(dāng)前沒有,父流程說了算。
繼承應(yīng)用到什么場景呢??
繼承應(yīng)用于業(yè)務(wù)處理的模式非常相似,只有中間處理環(huán)境不同的時(shí)候。 比如:
A B C D ---O--- -D -C -B -A
類型的業(yè)務(wù)處理流程,只有O不同,其他處理模式完全相同,此時(shí)采用繼承方式都非常舒服了,只要定義父流程,在子流程中只用定義O一個(gè)流程節(jié)點(diǎn)即可。以后要統(tǒng)一進(jìn)行流程調(diào)整,只要在父流程中進(jìn)行調(diào)整就可以了。
比如:flow aa定義為
<flow id="aa" name="aa">
<nodes>
<node id="begin">
<next-nodes>
<next-node component-result="begin" next-node-id="hello"/>
</next-nodes>
</node>
<node id="hello">
<component class-name="org.tinygroup.flow.HelloWorldComponent">
<properties>
<property name="name" value="world"/>
</properties>
</component>
<next-nodes>
<next-node next-node-id="end"/>
</next-nodes>
</node>
</nodes>
< /flow>
flow bb定義為
<flow id="bb" name="bb" extend-flow-id="aa">
< nodes>
< node id="hello">
< component class-name="org.tinygroup.flow.HelloWorldComponent">
< properties>
< property name="name" value="world" />
< /properties>
< /component>
< /node>
< /nodes>
< /flow>
則流程bb也可以順利執(zhí)行,且執(zhí)行結(jié)果是Hello, world
非常重要的一個(gè)亮點(diǎn)就是屬性賦值。
屬性賦值是否好用,決定了框架的易用性。
可以支持常量賦值"1"表示數(shù)字常量
aa 表示字符串常量可以支持,環(huán)境變量賦值
比如:xx表示從環(huán)境變量取xx鍵值的對象
可以支持屬性賦值
比如:xx.abc表示取環(huán)境變量xx的屬性abc
比如:xx.abc.def表示取環(huán)境變量xx的屬性abc的屬性def
可以支持組合賦值
比如:${in:aa.abc.def}-${in:bb.cc.dd}
表示把環(huán)境aa中的屬性abc的屬性def中間加"-"再加上環(huán)境變量bb中的cc的屬性的dd屬性
其中屬性的層次不受限制。
另外,取值方式,也支持自行擴(kuò)展:
比如:可以用${in:xmlkey.aa}也取在環(huán)境中xmlkey對應(yīng)的xml節(jié)點(diǎn)的aa屬性
所以,只有想不到的,沒有做不到的。
應(yīng)用開發(fā)與部署方式,比較典型的有B/S與B/A/S,C/A/S等。對于B/A/S和C/A/S方式,因?yàn)锳與B和C是分離部署的,所以,所有的內(nèi)容都需要是通過Context進(jìn)行傳遞的。
如果是通過分離式部署,那么就需要通過網(wǎng)絡(luò)來傳遞請求環(huán)境數(shù)據(jù)。
如果是想通過B/S環(huán)境來構(gòu)建系統(tǒng),此時(shí)就會期望通過HTTP處理線程來同布調(diào)用流程處理結(jié)果。
同時(shí),有時(shí)流程處理的數(shù)據(jù)可能是在Request,RequestAttribute,Session,Cookie中,如果把這些數(shù)據(jù)COPY到環(huán)境當(dāng)中去,其實(shí)是有較大的性能消耗的。
本流程引擎即支持通過服務(wù)方式調(diào)用,也可以通過短路方式進(jìn)行調(diào)用。
雖然我們推薦使用B/A/S體系架構(gòu),但是不能否認(rèn),目前我們的許多產(chǎn)品還是在B/S架構(gòu)下運(yùn)行的。
但是好在,這個(gè)對于流程引擎來說,他并不直接訪問Request和Session,Cookie等內(nèi)容,所以,即使是集成在一起部署,也不妨礙進(jìn)行分離式部署,依然可以保證服務(wù)的無狀態(tài)特性,前提就是需要實(shí)現(xiàn)一個(gè)Context的接口。
Tiny的流程引擎,提供了相當(dāng)強(qiáng)悍的功能及擴(kuò)展性,上面只說了一部分,有些也沒有完全說清楚,實(shí)際上,還提供了包含EL表達(dá)式等許多高級功能,對于期望進(jìn)行流程式編排開發(fā)來說,有相當(dāng)好的支持。 從后期效果來看,在Tiny框架中,業(yè)務(wù)流程編排及頁面流程編排都是基于此引擎構(gòu)建,應(yīng)用效果非常良好。