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