最近在研究業務視覺化的問題,在日常的工作中,流程圖和程式碼往往是分開管理的。
一個被維護多次的系統,到最後流程圖和程式碼是否匹配這個都很難說。
一轉眼三年過去了,目前這個想法已經逐步落地實現變成程式碼。
對於簡單的流程
a -> b -> c
可以很容易用程式碼來實現
// 執行a a(); // 執行b b(); // 執行c c();
對於並行的流程
a -> b a -> c
這個就要多執行緒框架來實現
// 執行a a(); // a結束後執行b new Thread(b).start(); // a結束後執行c new Thread(c).start();
對於分支合併的流程
a -> b a -> c b -> d c -> d
// 執行a a(); // a結束後執行b new Thread(b).start(); // a結束後執行c new Thread(c).start(); // 等待b,c結束 waitComplete(b,c); // 執行d d();
a(); b(); c(); d();
首先要有一個繪製流程圖的介面。並且能夠將流程圖轉化為json格式。
這裡我選擇了Vis.js的network。
可以編輯簡單流程,如下
還可以實現流程圖和json之間的互轉。
我們把這些節點的基本資訊拿到,就可以得到一張圖,然後通過程式遍歷這張圖的每個節點,即可達到執行流程圖的效果。
接下來就是流程圖的節點與Java的方法系結了。
我做了一個Annotation來繫結流程圖節點,
public @interface Node { String id() default "" ; String label() default "" ; }
節點得到執行開始事件後,拿到要執行的節點ID和名稱,查詢對應的類的Annotation對應的方法,如找到則執行該方法。
public int execute(String flowId, String nodeId, String historyId, HistoryNodeEntity nodeEntity) throws Exception{ String nodeName = nodeEntity.getNodeName(); System.out.println( "execute:" + nodeId); System.out.println( "node name:" + nodeEntity.getNodeName()); Method methods[] = this .getClass().getMethods(); if (methods != null ) { for (Method method:methods) { Node node = method.getAnnotation(Node. class ); if (node != null ) { if (node.id().equals(nodeId) || node.label().equals(nodeName)) { try { method.invoke( this ); return 0 ; } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); throw e; } } } } } return 0 ; }
我們需要做一個繼承自FlowRunner的類,裡面的方法和flow的節點繫結,和一個flow的組態檔,放在相同的目錄下。
MyFlow1.java
public class MyFlow1 extends FlowRunner { @Node (label= "a" ) public void process_a() { System.out.println( "processing a" ); } @Node (label= "b" ) public void process_b() { System.out.println( "processing b" ); } @Node (label= "c" ) public void process_c() { System.out.println( "processing c" ); } @Node (label= "d" ) public void process_c() { System.out.println( "processing d" ); } }
MyFlow1.json
{ "flowId" : "123" , "nodes" : [ { "id" : "1" , "label" : "start" }, { "id" : "2" , "label" : "a" }, { "id" : "0b5ba9df-b6c7-4752-94e2-debb6104015c" , "label" : "b" }, { "id" : "29bc32c7-acd8-4893-9410-e9895da38b2e" , "label" : "c" } ], "edges" : [ { "id" : "1" , "from" : "1" , "to" : "2" , "arrows" : "to" }, { "id" : "078ffa82-5eff-4d33-974b-53890f2c9a18" , "from" : "1" , "to" : "0b5ba9df-b6c7-4752-94e2-debb6104015c" , "arrows" : "to" }, { "id" : "90663193-7077-4aca-9011-55bc8745403f" , "from" : "2" , "to" : "29bc32c7-acd8-4893-9410-e9895da38b2e" , "arrows" : "to" }, { "id" : "a6882e25-c07a-4abd-907e-e269d4eda0ec" , "from" : "0b5ba9df-b6c7-4752-94e2-debb6104015c" , "to" : "29bc32c7-acd8-4893-9410-e9895da38b2e" , "arrows" : "to" } ] }
然後通過下面的程式碼來啟動流程。
MyFlow1 myFlow1 = new MyFlow1(); myFlow1.startFlow();
系統關閉時,通過下面的程式碼關閉流程管理器
FlowStarter.shutdown();
正常結束紀錄檔如下
Ready queue thread started.
Complete queue thread started.
json:
{"flowId":"123","nodes":[{"id":"1","label":"a"},{"id":"2","label":"b"},{"id":"0b5ba9df-b6c7-4752-94e2-debb6104015c","label":"c"},{"id":"29bc32c7-acd8-4893-9410-e9895da38b2e","label":"d"}],"edges":[{"id":"1","from":"1","to":"2","arrows":"to"},{"id":"078ffa82-5eff-4d33-974b-53890f2c9a18","from":"1","to":"0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows":"to"},{"id":"90663193-7077-4aca-9011-55bc8745403f","from":"2","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"},{"id":"a6882e25-c07a-4abd-907e-e269d4eda0ec","from":"0b5ba9df-b6c7-4752-94e2-debb6104015c","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"}]}
execute:1
node name:a
processing a
execute:2
node name:b
processing b
execute:0b5ba9df-b6c7-4752-94e2-debb6104015c
node name:c
processing c
execute:29bc32c7-acd8-4893-9410-e9895da38b2e
node name:d
processing d
Complete success.
json:
{"nodes":[{"id": "1","label": "a" ,"color": "#36AE7C"},{"id": "2","label": "b" ,"color": "#36AE7C"},{"id": "0b5ba9df-b6c7-4752-94e2-debb6104015c","label": "c" ,"color": "#36AE7C"},{"id": "29bc32c7-acd8-4893-9410-e9895da38b2e","label": "d" ,"color": "#36AE7C"}],"edges":[{"id": "1","from": "1","to": "2","arrows": "to"},{"id": "078ffa82-5eff-4d33-974b-53890f2c9a18","from": "1","to": "0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows": "to"},{"id": "90663193-7077-4aca-9011-55bc8745403f","from": "2","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"},{"id": "a6882e25-c07a-4abd-907e-e269d4eda0ec","from": "0b5ba9df-b6c7-4752-94e2-debb6104015c","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"}]}
流程執行結束後,會輸出執行結果和執行後的流程圖狀態。
可以直接將json貼到下面的位置,檢視看結果(綠色表示正常結束,紅色表示異常結束,白色表示等待執行)。
異常結束紀錄檔如下
Ready queue thread started.
Complete queue thread started.
json:
{"flowId":"123","nodes":[{"id":"1","label":"a"},{"id":"2","label":"b"},{"id":"0b5ba9df-b6c7-4752-94e2-debb6104015c","label":"c"},{"id":"29bc32c7-acd8-4893-9410-e9895da38b2e","label":"d"}],"edges":[{"id":"1","from":"1","to":"2","arrows":"to"},{"id":"078ffa82-5eff-4d33-974b-53890f2c9a18","from":"1","to":"0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows":"to"},{"id":"90663193-7077-4aca-9011-55bc8745403f","from":"2","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"},{"id":"a6882e25-c07a-4abd-907e-e269d4eda0ec","from":"0b5ba9df-b6c7-4752-94e2-debb6104015c","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"}]}
execute:1
node name:a
processing a
execute:2
node name:b
processing b
execute:0b5ba9df-b6c7-4752-94e2-debb6104015c
node name:c
processing c
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at io.github.nobuglady.network.fw.FlowRunner.execute(FlowRunner.java:49)
at io.github.nobuglady.network.fw.executor.NodeRunner.run(NodeRunner.java:93)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: test
at io.github.nobuglady.network.MyFlow1.process_b(MyFlow1.java:16)
... 11 more
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at io.github.nobuglady.network.fw.FlowRunner.execute(FlowRunner.java:49)
at io.github.nobuglady.network.fw.executor.NodeRunner.run(NodeRunner.java:93)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: test
at io.github.nobuglady.network.MyFlow1.process_b(MyFlow1.java:16)
... 11 more
Complete error.
json:
{"nodes":[{"id": "1","label": "a" ,"color": "#36AE7C"},{"id": "2","label": "b" ,"color": "#EB5353"},{"id": "0b5ba9df-b6c7-4752-94e2-debb6104015c","label": "c" ,"color": "#36AE7C"},{"id": "29bc32c7-acd8-4893-9410-e9895da38b2e","label": "d" ,"color": "#E8F9FD"}],"edges":[{"id": "1","from": "1","to": "2","arrows": "to"},{"id": "078ffa82-5eff-4d33-974b-53890f2c9a18","from": "1","to": "0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows": "to"},{"id": "90663193-7077-4aca-9011-55bc8745403f","from": "2","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"},{"id": "a6882e25-c07a-4abd-907e-e269d4eda0ec","from": "0b5ba9df-b6c7-4752-94e2-debb6104015c","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"}]}
流程執行結束後,會輸出執行結果和執行後的流程圖狀態。
可以直接將json貼到下面的位置,檢視看結果(綠色表示正常結束,紅色表示異常結束,白色表示等待執行)。
原始碼:https://github.com/nobuglady/nobuglady-network
感謝閱讀。歡迎Star。