Java各種規則引擎
更多內容關注公眾號:SAP Technical
一. Drools規則引擎
- 簡介:
Drools就是為了解決業務程式碼和業務規則分離的引擎。
Drools 規則是在 Java 應用程式上執行的,其要執行的步驟順序由程式碼確定
,為了實現這一點,Drools 規則引擎將業務規則轉換成執行樹。
- 特性:
優點:
1、簡化系統架構,優化應用
2、提高系統的可維護性和維護成本
3、方便系統的整合
4、減少編寫“硬程式碼”業務規則的成本和風險
3.原理:
Drools.png
- 使用方式:
(1)Maven 依賴:
<dependencies>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>6.5.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>6.5.0.Final</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
(2)新建配置檔案/src/resources/META-INF/kmodule.xml
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="rules" packages="rules">
<ksession name="myAgeSession"/>
</kbase>
</kmodule>
(3)新建drools規則檔案/src/resources/rules/age.drl
import cn.caijiajia.decision.domain.User // 匯入類
dialect "mvel"
rule "age" // 規則名,唯一
when
$user : User(age<15 || age>60) //規則的條件部分
then
System.out.println("年齡不符合要求!");
end
工程搭建完畢,效果如圖:
專案結構.png
測試用例:
package cn.caijiajia.decision.service;
import cn.caijiajia.decision.domain.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/26
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:applicationContext.xml"})
public class TestUser {
private static KieContainer container = null;
private KieSession statefulKieSession = null;
@Test
public void test(){
KieServices kieServices = KieServices.Factory.get();
container = kieServices.getKieClasspathContainer();
statefulKieSession = container.newKieSession("myAgeSession");
User user = new User("duval yang",12);
statefulKieSession.insert(user);
statefulKieSession.fireAllRules();
statefulKieSession.dispose();
}
}
二.Aviator表示式求值引擎
- 簡介:
Aviator是一個高效能、輕量級的java語言實現的表示式求值引擎,主要用於各
種表示式的動態求值。現在已經有很多開源可用的java表示式求值引擎,為什
麼還需要Avaitor呢?
Aviator的設計目標是輕量級和高效能 ,相比於Groovy、JRuby的笨重,Aviator
非常小,加上依賴包也才450K,不算依賴包的話只有70K;當然,Aviator的語法
是受限的,它不是一門完整的語言,而只是語言的一小部分集合。
其次,Aviator的實現思路與其他輕量級的求值器很不相同,其他求值器一般都
是通過解釋的方式執行,而Aviator則是直接將表示式編譯成Java位元組碼,交給
JVM去執行。簡單來說,Aviator的定位是介於Groovy這樣的重量級指令碼語言和
IKExpression這樣的輕量級表示式引擎之間。
- 特性:
(1)支援大部分運算操作符,包括算術操作符、關係運算子、邏輯操作符、
正則匹配操作符(=~)、三元表示式?: ,並且支援操作符的優先順序和括號強制優
先級,具體請看後面的操作符列表。
(2)支援函式呼叫和自定義函式。
(3)支援正規表示式匹配,類似Ruby、Perl的匹配語法,並且支援類Ruby的
$digit指向匹配分組。自動型別轉換,當執行操作的時候,會自動判斷運算元類
型並做相應轉換,無法轉換即拋異常。
(4)支援傳入變數,支援類似a.b.c的巢狀變數訪問。
(5)效能優秀。
(6)Aviator的限制,沒有if else、do while等語句,沒有賦值語句,僅支援邏
輯表示式、算術表示式、三元表示式和正則匹配。沒有位運算子
-
整體結構:
整體結構.png
-
maven依賴:
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>${aviator.version}</version>
</dependency>
- 執行方式
執行表示式的方法有兩個:execute()、exec();
execute(),需要傳遞Map格式引數
exec(),不需要傳遞Map
示例:
package cn.caijiajia.decision.controller;
import com.googlecode.aviator.AviatorEvaluator;
import java.util.HashMap;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
// exec執行方式,無需傳遞Map格式
String age = "18";
System.out.println(AviatorEvaluator.exec("'His age is '+ age +'!'", age));
// execute執行方式,需傳遞Map格式
Map<String, Object> map = new HashMap<String, Object>();
map.put("age", "18");
System.out.println(AviatorEvaluator.execute("'His age is '+ age +'!'",
map));
}
}
-
使用函式
Aviator可以使用兩種函式:內建函式、自定義函式
(1)內建函式Aviator內建函式.png
Aviator內建函式.png
package cn.caijiajia.decision.service;
import com.googlecode.aviator.AviatorEvaluator;
import java.util.HashMap;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
Map<String,Object> map = new HashMap<>();
map.put("s1","123qwer");
map.put("s2","123");
System.out.println(AviatorEvaluator.execute("string.startsWith(s1,s2)",map));
}
}
(2)自定義函式
自定義函式要繼承AbstractFunction類,重寫目標方法。
package cn.caijiajia.decision.controller;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorDouble;
import com.googlecode.aviator.runtime.type.AviatorLong;
import com.googlecode.aviator.runtime.type.AviatorObject;
import java.util.HashMap;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
// 註冊自定義函式
AviatorEvaluator.addFunction(new MultiplyFunction());
// 方式1
System.out.println(AviatorEvaluator.execute("multiply(12.23, -2.3)"));
// 方式2
Map<String, Object> params = new HashMap<>();
params.put("a", 12.23);
params.put("b", -2.3);
System.out.println(AviatorEvaluator.execute("multiply(a, b)", params));
}
}
class MultiplyFunction extends AbstractFunction{
@Override
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
double num1 = FunctionUtils.getNumberValue(arg1, env).doubleValue();
double num2 = FunctionUtils.getNumberValue(arg2, env).doubleValue();
return new AviatorDouble(num1 * num2);
}
@Override
public String getName() {
return "multiply";
}
}
-
常用操作符的使用
(1)操作符列表操作符列表.png
(2)常量和變數
常量和變數.png
(3)編譯表示式
package cn.caijiajia.decision.service;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import java.util.HashMap;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
String expression = "a+(b-c)>100";
// 編譯表示式
Expression compiledExp = AviatorEvaluator.compile(expression);
Map<String, Object> env = new HashMap<>();
env.put("a", 100.3);
env.put("b", 45);
env.put("c", -199.100);
// 執行表示式
Boolean result = (Boolean) compiledExp.execute(env);
System.out.println(result);
}
}
(4) 訪問陣列和集合
List和陣列用list[0]和array[0],Map用map.date
package cn.caijiajia.decision.service;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import java.util.*;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
final List<String> list = new ArrayList<>();
list.add("hello");
list.add(" world");
final int[] array = new int[3];
array[0] = 0;
array[1] = 1;
array[2] = 3;
final Map<String, Date> map = new HashMap<>();
map.put("date", new Date());
Map<String, Object> env = new HashMap<>();
env.put("list", list);
env.put("array", array);
env.put("map", map);
System.out.println(AviatorEvaluator.execute(
"list[0]+':'+array[0]+':'+'today is '+map.date", env));
}
}
(5) 三元比較符
package cn.caijiajia.decision.controller;
import com.googlecode.aviator.AviatorEvaluator;
import java.util.HashMap;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
Map<String, Object> env = new HashMap<String, Object>();
env.put("a", -5);
String result = (String) AviatorEvaluator.execute("a>0? 'yes':'no'", env);
System.out.println(result);
}
}
(6) 正規表示式匹配
package cn.caijiajia.decision.controller;
import com.googlecode.aviator.AviatorEvaluator;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
String email = "hello2018@gmail.com";
Map<String, Object> env = new HashMap<String, Object>();
env.put("email", email);
String username = (String) AviatorEvaluator.execute("email=~/([\\w0-8]+)@\\w+[\\.\\w+]+/ ? $1 : 'unknow' ", env);
System.out.println(username);
}
}
(7) 變數的語法糖衣
package cn.caijiajia.decision.controller;
import com.googlecode.aviator.AviatorEvaluator;
import java.util.HashMap;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
User user = new User(1,"jack","18");
Map<String, Object> env = new HashMap<>();
env.put("user", user);
String result = (String) AviatorEvaluator.execute(" '[user id='+ user.id + ',name='+user.name + ',age=' +user.age +']' ", env);
System.out.println(result);
}
}
package cn.caijiajia.decision.controller;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class User {
private int id;
private String name;
private String age;
public User() {
}
public User(int id, String name, String age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
(8) nil物件[任何物件都比nil大除了nil本身]
nil是Aviator內建的常量,類似java中的null,表示空的值。nil跟null不同的在
於,在java中null只能使用在==、!=的比較運算子,而nil還可以使用>、>=、
<、<=等比較運算子。Aviator規定,[任何物件都比nil大除了nil本身]。使用者傳入
的變數如果為null,將自動以nil替代。
AviatorEvaluator.execute("nil == nil"); //true
AviatorEvaluator.execute(" 3> nil"); //true
AviatorEvaluator.execute(" true!= nil"); //true
AviatorEvaluator.execute(" ' '>nil "); //true
AviatorEvaluator.execute(" a==nil "); //true,a is null
nil與String相加的時候,跟java一樣顯示為null
(9) 日期比較
package cn.caijiajia.decision.controller;
import com.googlecode.aviator.AviatorEvaluator;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/25
*/
public class Test {
public static void main(String[] args) {
Map<String, Object> env = new HashMap<String, Object>();
final Date date = new Date();
String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SS").format(date);
env.put("date", date);
env.put("dateStr", dateStr);
Boolean result = (Boolean) AviatorEvaluator.execute("date==dateStr",
env);
System.out.println(result);
result = (Boolean) AviatorEvaluator.execute("date > '2009-12-20
00:00:00:00' ", env);
System.out.println(result);
result = (Boolean) AviatorEvaluator.execute("date < '2200-12-20
00:00:00:00' ", env);
System.out.println(result);
result = (Boolean) AviatorEvaluator.execute("date ==date ", env);
System.out.println(result);
}
}
(10) 語法手冊
資料型別
-
Number型別:數字型別,支援兩種型別,分別對應Java的Long和Double,也就是說任何整數都將被轉換為Long,而任何浮點數都將被轉換為Double,包括使用者傳入的數值也是如此轉換。不支援科學計數法,僅支援十進位制。如-1、100、2.3等。
-
String型別: 字串型別,單引號或者雙引號括起來的文字串,如'hello world',變數如果傳入的是String或者Character也將轉為String型別。
-
Bool型別: 常量true和false,表示真值和假值,與java的Boolean.TRUE和Boolean.False對應。
-
Pattern型別: 類似Ruby、perl的正規表示式,以//括起來的字串,如//d+/,內部實現為java.util.Pattern。
-
變數型別: 與Java的變數命名規則相同,變數的值由使用者傳入,如"a"、"b"等
-
nil型別: 常量nil,類似java中的null,但是nil比較特殊,nil不僅可以參與==、!=的比較,也可以參與>、>=、<、<=的比較,Aviator規定任何型別都n大於nil除了nil本身,nil==nil返回true。使用者傳入的變數值如果為null,那麼也將作為nil處理,nil列印為null。
算術運算子
Aviator支援常見的算術運算子,包括+ - <tt></tt> / % 五個二元運算子,和一元運算子"-"。其中 - <tt></tt> / %和一元的"-"僅能作用於Number型別。
"+"不僅能用於Number型別,還可以用於String的相加,或者字串與其他物件的相加。Aviator規定,任何型別與String相加,結果為String。
邏輯運算子
Avaitor的支援的邏輯運算子包括,一元否定運算子"!",以及邏輯與的"&&",邏輯或的"||"。邏輯運算子的運算元只能為Boolean。
關係運算子
Aviator支援的關係運算子包括"<" "<=" ">" ">=" 以及"=="和"!=" 。
&&和||都執行短路規則。
關係運算子可以作用於Number之間、String之間、Pattern之間、Boolean之間、變數之間以及其他型別與nil之間的關係比較,不同型別除了nil之外不能相互比較。
Aviator規定任何物件都比nil大除了nil之外。
匹配運算子
匹配運算子"=~"用於String和Pattern的匹配,它的左運算元必須為String,右運算元必須為Pattern。匹配成功後,Pattern的分組將存於變數$num,num為分組索引。
三元運算子
Aviator沒有提供if else語句,但是提供了三元運算子 "?:",形式為 bool ? exp1: exp2。 其中bool必須為結果為Boolean型別的表示式,而exp1和exp2可以為任何合法的Aviator表示式,並且不要求exp1和exp2返回的結果型別一致。
- 兩種模式
預設AviatorEvaluator以編譯速度優先:
AviatorEvaluator.setOptimize(AviatorEvaluator.COMPILE);
你可以修改為執行速度優先,這會做更多的編譯優化:
AviatorEvaluator.setOptimize(AviatorEvaluator.EVAL);
三.MVEL表示式解析器
1.簡介 :
MVEL在很大程度上受到Java語法的啟發,作為一個表示式語言,也有一些根本
的區別,旨在更高的效率,例如:直接支援集合、陣列和字串匹配等操作以
及正規表示式。 MVEL用於執行使用Java語法編寫的表示式。
2.特性:
MVEL是一個功能強大的基於Java應用程式的表示式語言。
目前最新的版本是2.0,具有以下特性:
(1). 動態JIT優化器。當負載超過一個確保程式碼產生的閾值時,選擇性地產生字
節程式碼,這大大減少了記憶體的使用量。新的靜態型別檢查和屬性支援,允許整合
型別安全表達。
(2). 錯誤報告的改善。包括行和列的錯誤資訊。
(3). 新的指令碼語言特徵。MVEL2.0 包含函式定義,如:閉包,lambda定義,
標準迴圈構造(for, while, do-while, do-until…),空值安全導航操作,內聯with
-context運營 ,易變的(isdef)的測試運營等等。
(4). 改進的整合功能。迎合主流的需求,MVEL2.0支援基礎型別的個性化屬性處理器,整合到JIT中。
(5). 更快的模板引擎,支援線性模板定義,巨集定義和個性化標記定義。
(6). 新的互動式shell(MVELSH)。
(7). 缺少可選型別安全
(8). 整合不良,通常通過對映填入內容。沒有位元組碼不能運作用位元組碼生成編
譯時間慢,還增加了可擴充套件性問題;不用位元組碼生成執行時執行非常慢
(9). 記憶體消耗過大
(10). Jar巨大/依賴規模
3.原理:
與java不同,MVEL是動態型別(帶有可選分類),也就是說在原始檔中是沒有
型別限制的。一條MVEL表示式,簡單的可以是單個識別符號,複雜的則可能是
一個充滿了方法呼叫和內部集合建立的龐大的布林表示式。
4.使用方式:
maven引入jar:
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.3.1.Final</version>
</dependency>
測試:
package com.lrq.wechatdemo.utils;
import com.google.common.collect.Maps;
import org.mvel2.MVEL;
import java.util.Map;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/26
*/
public class MvelUtils {
public static void main(String[] args) {
String expression = "a == null && b == nil ";
Map<String,Object> map = Maps.newHashMap();
map.put("a",null);
map.put("b",null);
Object object = MVEL.eval(expression,map);
System.out.println(object);
}
}
四.EasyRules規則引擎
1.簡介:
easy-rules首先整合了mvel表示式,後續可能整合SpEL的一款輕量
級規則引擎
2.特性:
easy rules是一個簡單而強大的java規則引擎,它有以下特性:
輕量級框架,學習成本低
基於POJO
為定義業務引擎提供有用的抽象和簡便的應用
從原始的規則組合成複雜的規則
它主要包括幾個主要的類或介面:Rule,RulesEngine,RuleListener,Facts
還有幾個主要的註解:@Action,@Condition,@Fact,@Priority,@Rule
3.使用方式:
@Rule可以標註name和description屬性,每個rule的name要唯一,
如果沒有指定,則RuleProxy則預設取類名
@Condition是條件判斷,要求返回boolean值,表示是否滿足條件
@Action標註條件成立之後觸發的方法
@Priority標註該rule的優先順序,預設是Integer.MAX_VALUE - 1,值
越小越優先
@Fact 我們要注意Facts的使用。Facts的用法很像Map,它是客戶
端和規則檔案之間通訊的橋樑。在客戶端使用put方法向Facts中添
加資料,在規則檔案中通過key來得到相應的資料。
有兩種使用方式:
- java方式
首先先建立規則並標註屬性
package com.lrq.wechatdemo.rules;
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Rule;
import org.jeasy.rules.support.UnitRuleGroup;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/26
*/
public class RuleClass {
@Rule(priority = 1) //規則設定優先順序
public static class FizzRule {
@Condition
public boolean isFizz(@Fact("number") Integer number) {
return number % 5 == 0;
}
@Action
public void printFizz() {
System.out.print("fizz\n");
}
}
@Rule(priority = 2)
public static class BuzzRule {
@Condition
public boolean isBuzz(@Fact("number") Integer number) {
return number % 7 == 0;
}
@Action
public void printBuzz() {
System.out.print("buzz\n");
}
}
public static class FizzBuzzRule extends UnitRuleGroup {
public FizzBuzzRule(Object... rules) {
for (Object rule : rules) {
addRule(rule);
}
}
@Override
public int getPriority() {
return 0;
}
}
@Rule(priority = 3)
public static class NonFizzBuzzRule {
@Condition
public boolean isNotFizzNorBuzz(@Fact("number") Integer number) {
// can return true, because this is the latest rule to trigger according to
// assigned priorities
// and in which case, the number is not fizz nor buzz
return number % 5 != 0 || number % 7 != 0;
}
@Action
public void printInput(@Fact("number") Integer number) {
System.out.print(number+"\n");
}
}
}
然後客戶端呼叫
package com.lrq.wechatdemo.rules;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.core.RulesEngineParameters;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/26
*/
public class RuleJavaClient {
public static void main(String[] args) {
// 建立規則引擎
RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);
// 建立規則集並註冊規則
Rules rules = new Rules();
rules.register(new RuleClass.FizzRule());
rules.register(new RuleClass.BuzzRule());
rules.register(new RuleClass.FizzBuzzRule(new RuleClass.FizzRule(), new RuleClass.BuzzRule()));
rules.register(new RuleClass.NonFizzBuzzRule());
// 執行規則
Facts facts = new Facts();
for (int i = 1; i <= 100; i++) {
facts.put("number", i);
fizzBuzzEngine.fire(rules, facts);
System.out.println();
}
}
}
2.yml方式
resources目錄下新建fizzbuzz.yml
---
name: "fizz rule"
description: "print fizz if the number is multiple of 5"
priority: 1
condition: "number % 5 == 0"
actions:
- "System.out.println(\"fizz\")"
---
name: "buzz rule"
description: "print buzz if the number is multiple of 7"
priority: 2
condition: "number % 7 == 0"
actions:
- "System.out.println(\"buzz\")"
---
name: "fizzbuzz rule"
description: "print fizzbuzz if the number is multiple of 5 and 7"
priority: 0
condition: "number % 5 == 0 && number % 7 == 0"
actions:
- "System.out.println(\"fizzbuzz\")"
---
name: "non fizzbuzz rule"
description: "print the number itself otherwise"
priority: 3
condition: "number % 5 != 0 || number % 7 != 0"
actions:
- "System.out.println(number)"
客戶端呼叫:
package com.lrq.wechatdemo.rules;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.core.RulesEngineParameters;
import org.jeasy.rules.mvel.MVELRuleFactory;
import java.io.FileNotFoundException;
import java.io.FileReader;
/**
* CreateBy: Liurenquan
* CreateDate: 2018/12/26
*/
public class RuleYmlClient {
public static void main(String[] args) throws FileNotFoundException {
// create a rules engine
RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters);
// create rules
Rules rules = MVELRuleFactory.createRulesFrom(new FileReader("fizzbuzz.yml"));
// fire rules
Facts facts = new Facts();
for (int i = 1; i <= 100; i++) {
facts.put("number", i);
fizzBuzzEngine.fire(rules, facts);
System.out.println();
}
}
}
相關文章
- Java規則引擎 Easy RulesJava
- Java中最流行的幾種業務規則引擎簡介Java
- uwegeercken/jare:Java業務規則引擎(Jare)JARJava
- URule規則引擎
- 用 Java 構建簡單的規則引擎Java
- 在Java中用規則引擎模式替代ifelse - VitaliJava模式
- 如何學習Java的規則引擎模式? - plagovJava模式Go
- 規則引擎模式 - upperdine模式
- .NET RulesEngine(規則引擎)
- 風控規則引擎(一):Java 動態指令碼Java指令碼
- 架構 規則引擎 quartz架構quartz
- Drools 規則引擎應用
- Drools規則引擎簡介
- 決策表模式: 一種業務規則引擎實現方式模式
- j-easy/easy-rules: Java簡單的規則引擎Java
- 什麼是規則引擎? - martinfowler
- Evrete 規則引擎簡介 | baeldungVR
- 規則引擎Golang指南 – Mohit KhareGolang
- 快速整合和使用 drools 規則引擎
- 什麼是業務規則引擎?
- 全渠道營銷規則引擎案例
- Spring Boot + liteflow 規則引擎,太香了!Spring Boot
- 規則引擎在IoT的重要性?
- 規則引擎開發經驗分享 - reddit
- Devs--開源規則引擎介紹dev
- Drools 業務規則引擎的完整教程
- 如何用Go快速實現規則引擎Go
- Drools規則引擎實踐直白總結
- 規則引擎與ML模型的比較 - xLaszlo模型
- 數字化轉型中的規則引擎
- 一個規則引擎的視覺化方案視覺化
- 開放封閉原則與規則引擎設計模式 - devgenius設計模式dev
- 第2-4-4章 規則引擎Drools規則屬性-業務規則管理系統-元件化-中臺元件化
- 通過規則引擎跟蹤Java執行狀態的檢測工具:BytemanJava
- 【ASP.NET Core】MVC控制器的各種自定義:特性化的路由規則ASP.NETMVC路由
- Java中各種Log的使用Java
- Java 各種鎖的小結Java
- 掌握Java各種日誌框架Java框架