不知不覺間,開源項目已經做了快兩年了。涉及的東西也越來越多。我始終堅持着一個念頭,持續學習。此生最慶幸的一件事,便是我所爲之工作的,正好是我喜歡的。
說下背景吧,公司剛好在用規則引擎。然後就想着,做一個東西出來,簡化開發。還是挺有意思的吧。
MAVEN引入
老規矩,還是先通過maven把easy-drools引入到項目中
<dependency>
<groupId>io.github.xiaoyudeguang</groupId>
<artifactId>easy-drools</artifactId>
<version>4.0.30</version>
</dependency>
<!--日誌框架-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
<!--規則引擎依賴包-->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.35.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>7.35.0.Final</version>
</dependency>
1.JAVA轉規則
1.1 一段標準的Java代碼:
package com.zlyx.easy.start;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.executor.RuleExecutor;
import com.zlyx.easy.drools.validate.RuleValidator;
/**
* @prop message string 結束標記
*/
public class Rule {
/**
* rule
*
* @param context context
*
* @throws Exception
*/
public void rule(RuleContextDomain context) {
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "規則引擎測試運行結束");
}
}
沒什麼難度,相信大家一定都能看懂的。現在,我們的目標是把它轉化成一段drools規則引擎可以直接運行的腳本。
1.2 新建規則執行器(需要繼承RuleExecuto父類)
package com.zlyx.easy.start;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zlyx.easy.drools.domain.RuleContextDomain;
/**
* @prop message string 結束標記
*/
public class Rule extends RuleExecutor<RuleContextDomain> {
/**
* rule
*
* @param context context
*
* @throws Exception
*/
public void rule(RuleContextDomain context) {
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "規則引擎測試運行結束");
}
@Override
public RuleContextDomain initContext() throws Exception {
// TODO Auto-generated method stub
return new RuleContextDomain();
}
}
可以看到,我們讓Rule類繼承了RuleExecutor父類,並聲明瞭泛型規則上下文類RuleContextDomain(可以替換爲業務中的實際上下文類)。同時,實現了一個initContext()方法,很明顯是用來準備上下文條件的。
好了,前期條件我們已經準備好了。現在,我們需要想辦法讓規則執行器跑起來。
1.3 啓動代碼
package com.zlyx.easy.start;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
/**
* @prop message string 結束標記
*/
public class Rule extends RuleExecutor<RuleContextDomain> {
/**
* rule
*
* @param context context
*
* @throws Exception
*/
public void rule(RuleContextDomain context) {
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "規則引擎測試運行結束");
}
@Override
public RuleContextDomain initContext() throws Exception {
// TODO Auto-generated method stub
return new RuleContextDomain();
}
public static void main(String[] args) throws Exception {
RuleBuilder.RuleBody ruleBody = RuleBuilder.newBuilder("測試測試");
ruleBody.when("$ctx: RuleContextDomain();"); // 規則條件
ruleBody.then(Rule.class, "rule"); // 規則內容
RuleValidator.validate(ruleBody, new Rule());
}
}
到這裏,將一個普通的Java類轉換爲規則的準備工作就完成了。接下來,只需要執行代碼就可以了。
1.4 執行日誌
==============================================================================
輸出原始規則文本內容:
package com.zlyx.easy.start;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @prop message string 結束標記
*/
rule "$rule_name"
when
$ctx: RuleContextDomain();
then
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "規則引擎運行結束");
end
==============================================================================
輸出渲染後規則文本內容:
package com.zlyx.easy.start;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @prop message string 結束標記
*/
rule "測試測試"
when
$ctx: RuleContextDomain();
then
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "規則引擎運行結束");
end
==============================================================================
執行開始規則:
第1次輸出 ->a
第2次輸出 ->b
第3次輸出 ->a
第4次輸出 ->b
第5次輸出 ->a
第6次輸出 ->b
[main] INFO 規則引擎測試 - 規則引擎運行結束
規則執行完畢
==============================================================================
其中, "輸出原始規則文本內容"部分的代碼就是轉換並驗證腳本無錯誤後的Drools規則腳本。複製出來就可以直接交給Drools規則引擎執行了。
1.5 添加自定義屬性動態修改程序
接下里我們試試高級點的功能,對Rule類做一些修改:
package com.zlyx.easy.start;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
/**
* @prop message string 結束標記
*/
public class Rule extends RuleExecutor<RuleContextDomain> {
/**
* rule
*
* @param context context
*
* @throws Exception
*/
public void rule(RuleContextDomain context) {
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "規則引擎測試運行結束");
}
@Override
public RuleContextDomain initContext() throws Exception {
// TODO Auto-generated method stub
return new RuleContextDomain();
}
/**
*
* 自定義設置
*
* @param classBody
* @return
*/
public RuleBuilder.RuleBody doFilter(RuleBuilder.RuleBody ruleBody) {
ruleBody.importClass(getTClass());
ruleBody.placeholders("規則引擎測試運行結束", "$message");
ruleBody.placeholders("6", "$count"); // 將常量替換爲$符號引用的變量(規則導出爲java的時候會用到)
ruleBody.props("count", "10"); // 將$符號引用的替換爲變量常量(驗證規則的時候會用到)
ruleBody.props("message", "程序正常結束"); // 將$符號引用的替換爲變量常量(驗證規則的時候會用到)
return ruleBody;
}
public static void main(String[] args) throws Exception {
RuleBuilder.RuleBody ruleBody = RuleBuilder.newBuilder("測試測試");
ruleBody.when("$ctx: RuleContextDomain();"); // 規則條件
ruleBody.then(Rule.class, "rule"); // 規則內容
RuleValidator.validate(ruleBody, new Rule());
}
}
1.6 自定義屬性動態修改程序執行日誌
改造完畢,再次執行:
==============================================================================
輸出原始規則文本內容:
package com.zlyx.easy.start;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @prop message string 結束標記
*/
rule "$rule_name"
when
$ctx: RuleContextDomain();
then
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < $count; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "$message");
end
==============================================================================
輸出渲染後規則文本內容:
package com.zlyx.easy.start;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @prop message string 結束標記
*/
rule "測試測試"
when
$ctx: RuleContextDomain();
then
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 10; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "程序正常結束");
end
==============================================================================
執行開始規則:
第1次輸出 ->a
第2次輸出 ->b
第3次輸出 ->a
第4次輸出 ->b
第5次輸出 ->a
第6次輸出 ->b
第7次輸出 ->a
第8次輸出 ->b
第9次輸出 ->a
第10次輸出 ->b
[main] INFO 規則引擎測試 - 程序正常結束
規則執行完畢
==============================================================================
2.驗證規則腳本
2.1 項目下新增規則腳本文件
在src/main/resources下創建rule文件夾,在rule文件夾下創建rule.drl文件,將規則腳本複製進去。比如上面轉換出來的規則文件:
package com.zlyx.easy.start;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @prop message string 結束標記
*/
rule "$rule_name"
when
$ctx: RuleContextDomain();
then
Logger log = LoggerFactory.getLogger("規則引擎測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < $count; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "$message");
end
2.2 新建規則執行器(需要繼承RuleExecutor父類)
package com.zlyx.easy.start;
import java.util.HashMap;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
/**
* 導入規則並驗證
*
* @Author 趙光
* @Desc DroolsTest
* @Date 2020年5月11日
*/
public class DroolsExecutor extends RuleExecutor<RuleContextDomain> {
@Override
public RuleContextDomain initContext() throws Exception {
return new RuleContextDomain();
}
public static void main(String[] args) throws Exception {
RuleValidator.validate("測試測試", new DroolsExecutor());
}
}
2.3 執行DroolsExecutor類main方法,執行日誌:
==============================================================================
輸出原始規則文本內容:
import com.zlyx.easy.drools.domain.RuleContextDomain;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @prop message string 結束標記
*/
rule "$rule_name"
when
$ctx: RuleContextDomain();
then
Logger log = LoggerFactory.getLogger("$rule_name");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "$message");
end
==============================================================================
輸出渲染後規則文本內容:
import com.zlyx.easy.drools.domain.RuleContextDomain;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @prop message string 結束標記
*/
rule "測試測試"
when
$ctx: RuleContextDomain();
then
Logger log = LoggerFactory.getLogger("測試測試");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "$message");
end
==============================================================================
執行開始規則:
第1次輸出 ->a
第2次輸出 ->b
第3次輸出 ->a
第4次輸出 ->b
第5次輸出 ->a
第6次輸出 ->b
[main] INFO 測試測試 - $message
規則執行完畢
==============================================================================
同樣的,可以通過重寫RuleExecutor的doFilter()方法動態修改執行邏輯。
3. 規則文件轉JAVA類
3.1 項目下新增模板文件
在src/main/resources下創建rule文件夾,在rule文件夾下創建rule.vml文件,將下面的模板代碼複製進去:
package $package_name;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.executor.RuleExecutor;
import com.zlyx.easy.drools.validate.RuleValidator;
$import_package
public class $class_name extends RuleExecutor<$rule_context_domain> {
/**
* $method_name
*
* @param context context
*
* @throws Exception
*/
public void $method_name($rule_context_domain context) {
$method_body
}
@Override
public $rule_context_domain initContext() throws Exception {
return new $rule_context_domain();
}
public static void main(String[] args) throws Exception {
RuleBuilder.RuleBody ruleBody = RuleBuilder.newBuilder("$rule_name");
ruleBody.when("$ctx: $rule_context_domain();"); // 規則條件
ruleBody.then($class_name .class, "$method_name"); // 規則內容
//導入包
ruleBody.importClass($rule_context_domain .class);
ruleBody.importClass(Logger.class);
ruleBody.importClass(LoggerFactory.class);
//設置屬性
ruleBody.props("message", "執行完成");
RuleValidator.validate(ruleBody, new $class_name());
}
}
3.2 新建規則執行器(需要繼承RuleExecutor父類)
package com.zlyx.easy.start;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
/**
* 導入規則並驗證
*
* @Author 趙光
* @Desc DroolsTest
* @Date 2020年5月11日
*/
public class DroolsExecutor extends RuleExecutor<RuleContextDomain> {
@Override
public RuleContextDomain initContext() throws Exception {
return new RuleContextDomain();
}
public static void main(String[] args) throws Exception {
RuleValidator.toJava("測試測試");
}
}
執行代碼後就會在DroolsExecutor同級目錄下生成Rule.class文件。
3.3 生成Rule.class文件
package com.zlyx.easy.start;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.executor.RuleExecutor;
import com.zlyx.easy.drools.validate.RuleValidator;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @prop message string 結束標記
*/
public class Rule extends RuleExecutor<RuleContextDomain> {
/**
* rule
*
* @param context context
*
* @throws Exception
*/
public void rule(RuleContextDomain context) {
Logger log = LoggerFactory.getLogger("$rule_name");
try {
List<String> list = Arrays.asList("a", "b");
for (int i = 0; i < 6; i++) {
System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
log.info("{}", "$message");
}
@Override
public RuleContextDomain initContext() throws Exception {
return new RuleContextDomain();
}
public static void main(String[] args) throws Exception {
RuleBuilder.RuleBody ruleBody = RuleBuilder.newBuilder("規則引擎測試");
ruleBody.when("$ctx: RuleContextDomain();"); // 規則條件
ruleBody.then(Rule .class, "rule"); // 規則內容
//導入包
ruleBody.importClass(RuleContextDomain .class);
ruleBody.importClass(Logger.class);
ruleBody.importClass(LoggerFactory.class);
//設置屬性
ruleBody.props("message", "執行完成");
RuleValidator.validate(ruleBody, new Rule());
}
}
同樣的,可以通過重寫RuleExecutor的doFilter()方法動態修改執行邏輯。
然後呢,編輯ava代碼,java代碼生成規則並驗證,複製規則出來放到drools殷勤去執行,一條完整的生產線就出來啦。