easy-drools

不知不觉间,开源项目已经做了快两年了。涉及的东西也越来越多。我始终坚持着一个念头,持续学习。此生最庆幸的一件事,便是我所为之工作的,正好是我喜欢的。
说下背景吧,公司刚好在用规则引擎。然后就想着,做一个东西出来,简化开发。还是挺有意思的吧。

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殷勤去执行,一条完整的生产线就出来啦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章