Drools是一个基于java的规则引擎,可以将复杂多变的规则从硬编码中解放出来,以规则脚本的形式存放在文件中,使得规则的变更不需要修正代码重启服务就可以在线上环境生效。
一个规则通常包括三个部分:属性部分(attribute)、条件部分(LHS)和结果部分(RHS),对于一个完整的规则来说,这三个部分都是可选的,以下规则是合法的
rule "name"
when
then
end
when-then:LHS,当LHS为空时,规则引擎自动添加eval(true),即LHS为空的规则总是返回ture
=========================================================== attribute =============================================================================
salience:设置规则执行的优先级,数字越大优先级越高
no-loop:控制已经执行的规则条件再次满足时是否再次执行
activation-group:将若干个规则划为一个组,只要有一个规则被执行,其余规则都不会被执行。可以使用salience控制规则执行先后
$objName:Object([filed约束]) 绑定一个变量objecName,类型为Object,且满足[flied]中的约束
declare:在规则文件中定义一个新的Fact类型
declare User
username:String
age:Integer
end
date-expires:当系统时间<=date-expires后才会触发,默认格式dd-MM-yyyy,使用System.setProperty("drools.dateformat","yyyy-MM-dd")设置其他类型的格式
rule "dateRule"
date-expires "2020-04-06"
when
eval(true);
then
System.out.println("dateRule is execution");
end
dete-effective:当系统时间>=date-effective设置的时间值时,规则才会触发执行。date-effective可接受的日期格式为'dd-MMM-yyyy'
rule 'dete-effective-test'
date-effective '12-04-2020'
when
eval(true)
then
System.out.println("rule is execution")
end
enabled:定义一个规则是否可用,默认是true
dialect:定义规则中使用的语言类型,目前drools支持两种语言类型:mvel和java。默认使用java
duration:如果设置了该属性,那么规则将在该属性值之后在另外线程中触发,单位毫秒
rule "duration-test"
duration 3000
when
eval(true)
then
System.out.println("rule threadId:"+Thread.currrentThread().getId());
end
lock-on-active:确实规则只执行一次。默认为false。将lock-on-active属性设置为true,可以避免某些Fact对象因属性改变而使已经执行过的规则再次激活执行
=============================================================== LHS ===============================================================================
contains:检查一个Fact对象的某个字段(该字段要是一个Collection或者Array类型)是否包含指定对象【not contains同理】
when
$order:Order()
$Customer:Customer(age >20,orders contains $order)
then
System.out.println($Customer.getName());
end
//输出年龄大于20且订单包含在Order中的CustomerName
memberOf 判断某个Fact对象的某个字段是否在一个集合(Collection/Array)当中。Object(filedName memberOf value[Collection/Array])【not memberOf】
when
$s:String()/List() //如果类型是String,则比较字符串中是否包含比较字符串,效果等同于contains
$c:Member(name memberOf $s);
then
System.out.println("memberOfRule 规则触发");
matches 用来对某个Fact的字段与标准的Java正则表达式进行相似匹配,被比较的字符串可以是一个标准的正则表达式,正则表达式不用考虑\的转义问题【not matches】
when
$customer:Customer(name matches '李.*');
then
statement;
==================================================== RHS =====================================================
insert(new Object()) 一旦调用insert函数,drools会重新与所有的规则重新匹配一次,对于没有设置no-loop属性为true的规则,如果条件满足,不管其之前是否执行过都会再执行一次。update、retract同样具有该特性
when
eval(true)
then
Customer cus = new Customer();
cus.setName("张三");
insert(cus);
end
update 对当前working memory中Fact对象进行更新。
retract 将当前Fact对象从working memory当中删除
modify 表达式块,可以快速实现对Fact对象多个属性进行修改,修改完成后自动跟新到当前的Working Memory当中。使用modify同样会导致引擎重新检查所有的规则匹配条件
语法格式:
modify(fact-express){
<修改Fact属性的表达式>[,<修改Fact属性的表达式>]
}
when
$customer:Customer(name=="张三",age=20)
then
System.out.println("modify before customer id:"+$customer.getId()+",age:"+$customer.getAge());
modify($customer){
setId("super man"),
setAge(30)
}
end
3.1.新建SpringBoot工程,导入以下maven依赖
org.drools
drools-core
7.6.0.Final
org.drools
drools-compiler
7.6.0.Final
org.drools
drools-templates
7.6.0.Final
org.kie
kie-api
7.6.0.Final
org.kie
kie-spring
7.6.0.Final
3.2.编写规则引擎配置类
@Configuration
public class RuleEngineConfig {
private static final Logger logger = LoggerFactory.getLogger(RuleEngineConfig.class);
private static final String RULES_PATH="droolsRule/"; //规则文件路径
private final KieServices kieServices = KieServices.Factory.get();
@Bean
public KieFileSystem kieFileSystem() throws IOException {
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] files = resourcePatternResolver.getResources(RULES_PATH + "*.*");
String path = null;
for (Resource file : files) {
path = RULES_PATH + file.getFilename();
logger.info("path="+path);
kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
}
return kieFileSystem;
}
@Bean
public KieContainer kieContainer() throws IOException {
KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
}
@Bean
public KieBase kieBase() throws IOException {
return kieContainer().getKieBase();
}
@Bean
public KieSession kieSession() throws IOException {
return kieContainer().newKieSession();
}
@Bean
public KModuleBeanFactoryPostProcessor kiePostProcessor() {
return new KModuleBeanFactoryPostProcessor();
}
}
3.3.编写一个简单规则,举例:将年龄大于20的用户信息输出
package droolsRule; //对应3.2配置类中的规则路径,规则文件放于文件夹a下,这里就写package a
import com.duang.droolsdemo.entity.Customer
rule "paramCheckNotNull"
salience 100
// 规则优先级,值越大优先级越高
when
$customer:Customer(age >= 20)
then
System.out.println($customer.toString());
end
3.4.编写测试类
@SpringBootTest
class DroolsdemoApplicationTests {
@Resource
private KieSession kieSession;
@Test
void contextLoads() {
}
@Test
public void testDrools(){
Customer customer = new Customer();
customer.setName("Alice");
customer.setAge(20);
customer.setTime(new Timestamp(System.currentTimeMillis()));
//调用规则引擎验证数据
kieSession.insert(customer);
kieSession.fireAllRules();
}
}
刚开始接触规则引擎的时候有个疑问,像上面的例子直接使用if-else不是更简单快捷么?后来使用过程中发现了有一个好处就是如果对规则文件进行修改无需重新编译程序即可生效甚至你还可以将规则存于数据库更灵活的修改规则。总而言之,规则引擎的水很深,我也才刚接触它,写篇博客分享心得也给以后做个留念。写的很仓促,疏漏错误之处还请大家多多指教,加油