一.前言
网上规则引擎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
扫描二维码关注公众号,回复:
766782 查看本文章
csdn资源下载地址:
https://download.csdn.net/download/vipshop_fin_dev/10296393
还有一些关于安全性及与spring的封装思路,作者表示还会继续完善,并还不是最终版本,有需要可以持续关注-_-!
与当前文章有关联的链接
vipshop_ebs/朱杰
2018-03-19