一.前言
網上規則引擎drools介紹很多,並且有很多細致的說明,作者也不敢托大說自己的好用,但作者經過2個項目使用過規則引擎后,自己對規則引擎的理解並進行封裝,對規則內容及如何使用,有自己的一番實踐,並提供源代碼,供大家參考
二.設計思路及具體代碼
首先我希望是輕量級的使用drools,不希望使用太過復雜的架構,以這種思路入手,就可以與任何項目輕易融入,這樣就需要能夠將drools規則的來源,從規則文件(drl)中可以從maven和自己的數據庫中獲取,腳本是動態的,配合drools6.5支持通過maven接入的規則的API,就可以做到從任何處獲取規則內容
一個工具類DroolsUtils
-
package com.vip.jie.rule.util;
-
-
import org.drools.compiler.kie.builder.impl.InternalKieModule;
-
import org.kie.api.KieServices;
-
import org.kie.api.builder.KieBuilder;
-
import org.kie.api.builder.KieFileSystem;
-
import org.kie.api.builder.ReleaseId;
-
import org.kie.api.builder.model.KieBaseModel;
-
import org.kie.api.builder.model.KieModuleModel;
-
import org.kie.api.builder.model.KieSessionModel;
-
import org.kie.api.conf.EqualityBehaviorOption;
-
import org.kie.api.conf.EventProcessingOption;
-
-
import java.io.IOException;
-
-
-
-
/**
-
* 動態生成kjar
-
* @author jie01.zhu
-
* @DateTime 2018/3/19 22:14
-
*/
-
public
class DroolsUtils {
-
-
/**
-
* 創建默認的kbase和stateful的kiesession
-
*
-
* @param ks
-
* @param isdefault
-
* @return
-
*/
-
private static KieFileSystem createKieFileSystemWithKProject(KieServices ks, boolean isdefault) {
-
KieModuleModel kproj = ks.newKieModuleModel();
-
KieBaseModel kieBaseModel1 = kproj.newKieBaseModel(
"KBase").setDefault(isdefault)
-
.setEqualsBehavior(EqualityBehaviorOption.EQUALITY)
-
.setEventProcessingMode(EventProcessingOption.STREAM);
-
// Configure the KieSession.
-
kieBaseModel1.newKieSessionModel(
"KSession").setDefault(isdefault)
-
.setType(KieSessionModel.KieSessionType.STATEFUL);
-
KieFileSystem kfs = ks.newKieFileSystem();
-
kfs.writeKModuleXML(kproj.toXML());
-
return kfs;
-
}
-
-
/**
-
* 創建kjar的pom
-
*
-
* @param releaseId
-
* @param dependencies
-
* @return
-
*/
-
private static String getPom(ReleaseId releaseId, ReleaseId... dependencies) {
-
String pom =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-
+
"<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
-
+
" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n"
-
+
" <modelVersion>4.0.0</modelVersion>\n" +
"\n" +
" <groupId>" + releaseId.getGroupId()
-
+
"</groupId>\n" +
" <artifactId>" + releaseId.getArtifactId() +
"</artifactId>\n" +
" <version>"
-
+ releaseId.getVersion() +
"</version>\n" +
"\n";
-
if (dependencies !=
null && dependencies.length >
0) {
-
pom +=
"<dependencies>\n";
-
for (ReleaseId dep : dependencies) {
-
pom +=
"<dependency>\n";
-
pom +=
" <groupId>" + dep.getGroupId() +
"</groupId>\n";
-
pom +=
" <artifactId>" + dep.getArtifactId() +
"</artifactId>\n";
-
pom +=
" <version>" + dep.getVersion() +
"</version>\n";
-
pom +=
"</dependency>\n";
-
}
-
pom +=
"</dependencies>\n";
-
}
-
pom +=
"</project>";
-
return pom;
-
}
-
-
/**
-
* 初始化一個kjar:把原有的drl包含進新建的kjar中
-
*
-
* @param ks
-
* @param releaseId
-
* @return
-
* @throws IOException
-
*/
-
public static InternalKieModule initKieJar(KieServices ks, ReleaseId releaseId) throws IOException {
-
KieFileSystem kfs = createKieFileSystemWithKProject(ks,
true);
-
kfs.writePomXML(getPom(releaseId));
-
KieBuilder kieBuilder = ks.newKieBuilder(kfs);
-
if (!kieBuilder.buildAll().getResults().getMessages().isEmpty()) {
-
throw
new IllegalStateException(
"Error creating KieBuilder.");
-
}
-
return (InternalKieModule) kieBuilder.getKieModule();
-
}
-
-
public static InternalKieModule createKieJar(KieServices ks, ReleaseId releaseId, DroolsResource droolsResource) {
-
KieFileSystem kfs = createKieFileSystemWithKProject(ks,
true);
-
kfs.writePomXML(getPom(releaseId));
-
kfs.write(
"src/main/resources/" + droolsResource.getTargetResourceName(), droolsResource.getResource());
-
-
KieBuilder kieBuilder = ks.newKieBuilder(kfs);
-
if (!kieBuilder.getResults().getMessages().isEmpty()) {
-
throw
new IllegalStateException(
-
"Error creating KieBuilder. errorMsg:" + kieBuilder.getResults().getMessages());
-
}
-
return (InternalKieModule) kieBuilder.getKieModule();
-
}
-
-
-
}
重點調用參考:
-
String fileName =
"jie-" + group +
"-rules";
-
/**
-
* 指定kjar包
-
*/
-
final ReleaseId releaseId = kieServices.newReleaseId(
"com.vip.jie", fileName,
"1.0.0");
-
-
log.info(
"DroolsGetKieSession fileName:{}", fileName);
-
log.info(
"[DroolsGetKieSession] drlStr:{}", drlStr);
-
// 創建初始化的kjar
-
InternalKieModule kJar = DroolsUtils.createKieJar(kieServices, releaseId,
-
new DroolsResource(ResourceFactory.newByteArrayResource(drlStr.getBytes()),
-
fileName +
".drl"));
其次再將如何讓使用者可以很靈活的接入,利用可變參數類型,將入參對象無限制傳入,由規則腳本去決定入參的變化,通過此種方式可以比較好的實現業務靈活接入
如下單元測試類:
-
package com.vip.jie.rule.service.impl;
-
-
-
import com.vip.jie.rule.object.User;
-
import com.vip.jie.rule.service.RuleManager;
-
import lombok.extern.slf4j.Slf4j;
-
import org.junit.Test;
-
-
import
static junit.framework.TestCase.fail;
-
-
/**
-
* 規則測試
-
* @author jie01.zhu
-
* @DateTime 2018/3/19 21:17
-
*
-
*/
-
@Slf4j
-
public
class RuleManagerImplTest {
-
-
private RuleManager ruleManager =
new RuleManagerImpl();
-
-
private String getRuleContent() {
-
StringBuilder stringBuilder =
new StringBuilder();
-
-
stringBuilder.append(
"import com.vip.jie.rule.object.User;\n");
-
stringBuilder.append(
"rule test1 when\n");
-
stringBuilder.append(
"user : User(age==20)\n");
-
stringBuilder.append(
"then\n");
-
stringBuilder.append(
"user.setName(\"張三\");\n");
-
stringBuilder.append(
"end\n");
-
return stringBuilder.toString();
-
}
-
-
@Test
-
public void executeRule1() throws Exception {
-
-
User user =
new User();
-
user.setAge(
20);
-
-
//調用規則
-
ruleManager.executeRule(
"test1", getRuleContent(), user);
-
log.info(
"test result:{}", user.toString());
-
-
if (!
"張三".equals(user.getName())) {
-
fail(
"error rule");
-
}
-
}
-
-
@Test
-
public void executeRule2() throws Exception {
-
-
User user =
new User();
-
user.setAge(
21);
-
-
//調用規則
-
ruleManager.executeRule(
"test1", getRuleContent(), user);
-
log.info(
"test result:{}", user.toString());
-
-
if (
"張三".equals(user.getName())) {
-
fail(
"error rule");
-
}
-
}
-
}
三.對應示例源代碼獲取方式
githup:
https://github.com/jie01/drools-demo
csdn資源下載地址:
https://download.csdn.net/download/vipshop_fin_dev/10296393
還有一些關於安全性及與spring的封裝思路,作者表示還會繼續完善,並還不是最終版本,有需要可以持續關注-_-!
與當前文章有關聯的鏈接
vipshop_ebs/朱傑
2018-03-19
原文地址:https://blog.csdn.net/vipshop_fin_dev/article/details/79618067