欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

规则引擎drools封装

程序员文章站 2022-05-28 12:24:08
...

一.前言

    网上规则引擎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


相关标签: 规则引擎