JAVA规则引擎 Drools 教程

JAVA herman 4032浏览 0评论
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

关于 Drools 框架很久了,一直没来得及整理这方面的资料。最近有人在微信公众号问到了我,于是我便抽了些时间,整理了这篇教程。希望能对大家有所帮助!

我们的日常生活是由规则驱动的。每次我们在开车的时候停在红灯处,之所以这样做,因为我们遵循一条规则,灯变红时我们应该停下来。如果你跳起来,最终会落到地面,这是由地球引力所决定的,它可以被表示成简单的数学方程。然而,对于我们生活日常中的规则,我们使用更加简单的结构来表示:当 XXX 的时候,做 XXX 的事。

when 满足什么样的条件
then 最终结果

在程序中普遍的存在一些业务逻辑规则。比如下面的代码:

if(){}
else if(){}
else{}

在这些规则不变的情况下,我们的程序能完成正常的需求规则。但是程序中的一些规则经常是需要变动的,这些变动我们可以通过代码来更改。但是往往一些需求规则变动太频繁,常改代码不是常事,因此我们需要一种更灵活的方法来实现。在这时 Drools 规则引擎框架诞生了,它能极大的方便我们更改一些程序中的规则。

Drools 简介

JBoss Rules 的前身是Codehaus的一个开源项目叫 Drools。最近被纳入JBoss门下,更名为JBoss Rules,成为了JBoss应用服务器的规则引擎。

Drools 是为 Java 量身定制的基于Charles Forgy的RETE算法的规则引擎的实现。具有了OO接口的RETE,使得商业规则有了更自然的表达。

Drools 的用 XML 的<Conditons>、<Consequence> 节点表达If–Then句式,而里面可以嵌入上述语言的代码作为判断语句和执行语句。

其中 Java 代码会使用Antlr进行解释,而 Groovy 和 Python 本身就是脚本语言,可以直接调用。

Drools 的聪明之处在于,用 XML 节点来规范 If–Then 句式和事实的定义,使引擎干起活来很舒服。

而使用 Java,Groovy等原生语言来做判断和执行语句,让程序员很容易过渡、移植,学习曲线很低。

下面我们进入实战系列,实现一个手机话费的例子。

定义规则

首先要分析自己的业务逻辑,根据业务而制定出不同的规则,这里我们假设有3个规则。

  1. 对于新开户的手机用户送20元话费。
  2. 在2014年10月到12期间充值的用户,不论金额多少充值满3次就赠送5元话费。
  3. 当月充值金额达到100以上,每达到一次赠送话费10元。

Drools 教程

分析这些规则,并把他们抽象成一个 EntityRule 对象。

package com.core.drools;
import java.util.UUID;
/**
* EntityRule-Model
* @author www.xttblog.com
*/
public class EntityRule {
	private String username;
	private boolean account;
	private int addtime;
	private double currentmoney;
	private double totailaddmoney;
	public void getSerialnumber(String username,double currentmoney){
		System.out.println("Account:"+username+" Balance:¥"+currentmoney);
		System.out.println("Serial Number:"+UUID.randomUUID().toString());
	}
}

定义规则引擎

业务和规则都整理清楚了我们就可以开始规则引擎的核心部分啦,这里我定义的是接口和实现类。

package com.core.drools;
/**
* RuleEngine-Interface
* @author www.xttblog.com
*/
public interface RuleEngine {
	public void init();
	public void refresh();
	public void execute(final EntityRule entityRule);
}

实现规则引擎

package com.core.drools;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import org.drools.RuleBase;
import org.drools.StatefulSession;
import org.drools.compiler.DroolsParserException;
import org.drools.compiler.PackageBuilder;
import org.drools.rule.Package;
import org.drools.spi.Activation;
/**
* RuleEngine-Implements
* @author www.xttblog.com
*/
public class RuleEngineImpl implements RuleEngine{
	private RuleBase ruleBase;
	@Override
	public void init() {
		System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm:ss");
		ruleBase =SingleRuleFactory.getRuleBase();
		try {
			PackageBuilder backageBuilder = getPackageBuilderFile();
			ruleBase.addPackages(backageBuilder.getPackages());
		} catch (DroolsParserException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	@Override
	public void refresh() {
		ruleBase = SingleRuleFactory.getRuleBase();
		Package[] packages = ruleBase.getPackages();
		for(Package items :packages){
			ruleBase.removePackage(items.getName());
		}
		init();
	}
	@Override
	public void execute(final EntityRule entityRule) {
		if(null == ruleBase.getPackages() || 0 == ruleBase.getPackages().length) {
			return;
		}
		StatefulSession statefulSession = ruleBase.newStatefulSession();
		statefulSession.insert(entityRule);
		statefulSession.fireAllRules(new org.drools.spi.AgendaFilter() {
			public boolean accept(Activation activation) {
				return !activation.getRule().getName().
				contains("_test");
			}
		});
		statefulSession.dispose();
	}
	private PackageBuilder getPackageBuilderFile()throws Exception {
		List<String> drlFilePath = getRuleFile();
		List<Reader> readers = loadRuleFile(drlFilePath);
		PackageBuilder backageBuilder = new PackageBuilder();
		for (Reader r : readers) {
			backageBuilder.addPackageFromDrl(r);
		}
		if(backageBuilder.hasErrors()) {
			throw new Exception(backageBuilder.getErrors().toString());
		}
		return backageBuilder;
	}
	private List<Reader> loadRuleFile(List<String> drlFilePath)throws FileNotFoundException {
		if (null == drlFilePath || 0 == drlFilePath.size()) {
			return null;
		}
		List<Reader> readers = new ArrayList<Reader>();
		for (String ruleFilePath : drlFilePath) {
			readers.add(new FileReader(new File(ruleFilePath)));
		}
		return readers;
	}
	private List<String> getRuleFile(){
		List<String> drlFilePath = new ArrayList<String>();
		String path="D:/utils/my/DroolsProject/src/
		com/core/drools/drools_rule.drl";
		drlFilePath.add(path);
		return drlFilePath;
	}
}

再定义一个单例的RuleBase工厂类。

package com.core.drools;
import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
/**
* @author www.xttblog.com
*/
public class SingleRuleFactory {
	private static RuleBase ruleBase;
	public static RuleBase getRuleBase(){
		return null != ruleBase ? ruleBase : RuleBaseFactory.newRuleBase();
	}
}

编写规则文件

规则文件可以根据自己的业务需求定义多个文件,这里我只定义了一个。

package com.core.drools
import com.core.drools.EntityRule;
rule accountEntity
//One
salience 100
lock-on-active true
when
$entityRule : EntityRule(account == true)
then
System.out.println("The new account:Present¥20.0");
$entityRule.setCurrentmoney($entityRule.getCurrentmoney()+20);
$entityRule.getSerialnumber($entityRule.getUsername(),$entityRule.getCurrentmoney());
System.out.println();
end
rule billEntity
//two
salience 99
lock-on-active true
date-effective "2014-010-01 00:00:00"
date-expires "2014-012-31 23:59:59"
when
$entityRule : EntityRule(addtime >= 3)
then
System.out.println("Prepaid phone numberreach "+$entityRule.getAddtime()+" times:Present ¥"+$entityRule.getAddtime()/3*5);
$entityRule.setCurrentmoney($entityRule.getCurrentmoney()+$entityRule.getAddtime()/3*5);
$entityRule.getSerialnumber($entityRule.getUsername(),$entityRule.getCurrentmoney());
System.out.println();
end
rule addMoney
//Three
salience 98
lock-on-active true
when
$entityRule : EntityRule(totailaddmoney >= 100)
then
System.out.println("The account for the month top-up totail amount is "+$entityRule.getTotailaddmoney()+":Present ¥"+(int)$entityRule.getTotailaddmoney()/100*10);
$entityRule.setCurrentmoney($entityRule.getCurrentmoney()+(int)$entityRule.getTotailaddmoney()/100 * 10);$entityRule.getSerialnumber($entityRule.getUsername(),$entityRule.getCurrentmoney());
end

测试引擎

package com.test;
import java.io.IOException;
import com.core.drools.EntityRule;
import com.core.drools.RuleEngine;
import com.core.drools.RuleEngineImpl;
/**
* Test Drools
* @author www.xttblog.com
*/
public class DroolsTest {
	public static void main(String[] args) throws IOException {
		RuleEngine engineImpl =new RuleEngineImpl();
		engineImpl.init();
		final EntityRule entityRule= new EntityRule();
		entityRule.setCurrentmoney(350d);
		entityRule.setUsername("Candy");
		entityRule.setAccount(true);
		entityRule.setTotailaddmoney(350d);
		entityRule.setAddtime(7);
		engineImpl.execute(entityRule);
	}
}

测试结果

The new account:Present ¥20.0
Account:Candy Balance:¥370.0
Serial Number:0fd98593-caa2-444d-a4ff-b4001cfb3260
------------------------------------------------------------------------------
Prepaid phone number reach 7 times:Present ¥10
Account:Candy Balance:¥380.0
Serial Number:a118b211-c28e-4035-aa60-2f417f62b2f3
------------------------------------------------------------------------------
The account for the month top-up totail amount is 350.0: Present ¥30
Account:Candy Balance:¥410.0
Serial Number:2bfc02c2-267f-47a2-9cda-5a4196e2b7cf

Drools 规则引擎就是这么简单,在实际的程序中能帮助我们解决很多现实问题。

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847(已满),QQ3群:187424846(已满)。QQ群进群密码:xttblog,想加微信群的朋友,之前的微信号好友已满,请加博主新的微信号:xttblog,备注:“xttblog”,添加博主微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » JAVA规则引擎 Drools 教程