关于规则文件整理的笔记(初版)

规则引擎

Drools规则加载方式

  • 规则文件.drl加载
  • excel决策表中加载
  • 构造一个字符串加载

Drools规则引擎

快速入门

第一步:创建maven工程drools_quickstart并导入drools相关maven坐标

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.10.0.Final</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

添加pom依赖

第二步:根据drools要求创建resources/META-INF/kmodule.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <!--
        name:指定kbase的名称,可以任意,但是需要唯一
        packages:指定规则文件的目录,需要根据实际情况填写,否则无法加载到规则文件
        default:指定当前kbase是否为默认
    -->
    <kbase name="myKbase1" packages="rules" default="true">
        <!--
            name:指定ksession名称,可以任意,但是需要唯一
            default:指定当前session是否为默认
        -->
        <ksession name="ksession-rule" default="true"/>
    </kbase>
</kmodule>

注意:上面配置文件的名称和位置都是固定写法,不能更改

第三步:创建实体类Order

package com.itheima.drools.entity;
/**
 * 订单
 */
public class Order {
    private Double originalPrice;//订单原始价格,即优惠前价格
    private Double realPrice;//订单真实价格,即优惠后价格
    public String toString() {
        return "Order{" +
                "originalPrice=" + originalPrice +
                ", realPrice=" + realPrice +
                '}';
    }
    public Double getOriginalPrice() {
        return originalPrice;
    }
    public void setOriginalPrice(Double originalPrice) {
        this.originalPrice = originalPrice;
    }
    public Double getRealPrice() {
        return realPrice;
    }
    public void setRealPrice(Double realPrice) {
        this.realPrice = realPrice;
    }
}

第四步:创建规则文件resources/rules/bookDiscount.drl

规则场景 :

100元以下没有优惠;

100元以上200元以下优惠20元

200元以上(包含)300元以下(包含)优惠50元

300元以上优惠100元

//图书优惠规则
package book.discount
import com.itheima.drools.entity.Order

//规则一:所购图书总价在100元以下的没有优惠
rule "book_discount_1"   //需要唯一
    when   //后面跟条件
        $order:Order(originalPrice < 100)
    then  //符合上面条件则执行后面操作
        $order.setRealPrice($order.getOriginalPrice());
        System.out.println("成功匹配到规则一:所购图书总价在100元以下的没有优惠");
end  //一个规则结束

//规则二:所购图书总价在100到200元的优惠20元
rule "book_discount_2"
    when
        $order:Order(originalPrice < 200 && originalPrice >= 100)
    then
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("成功匹配到规则二:所购图书总价在100到200元的优惠20元");
end

//规则三:所购图书总价在200到300元的优惠50元
rule "book_discount_3"
    when
        $order:Order(originalPrice <= 300 && originalPrice >= 200)
    then
        $order.setRealPrice($order.getOriginalPrice() - 50);
        System.out.println("成功匹配到规则三:所购图书总价在200到300元的优惠50元");
end

//规则四:所购图书总价在300元以上的优惠100元
rule "book_discount_4"
    when
        $order:Order(originalPrice > 300)
    then
        $order.setRealPrice($order.getOriginalPrice() - 100);
        System.out.println("成功匹配到规则四:所购图书总价在300元以上的优惠100元");
end

第五步:编写单元测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iA72OSXK-1684140681032)(项目数据管理.assets/image-20230330094723718.png)]

@Test
public void test1(){
    //第一步 :获取服务
    KieServices kieServices = KieServices.Factory.get();
    //第二步:通过服务获取容器
    KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
    //第三步: 通过容器获取kieSession,由KieSession去和规则引擎交互(会话对象,用于和规则引擎交互)
    KieSession kieSession = kieClasspathContainer.newKieSession();

    //构造订单对象(事实对象),设置原始价格,由规则引擎根据优惠规则计算优惠后的价格
    Order order = new Order();
    order.setOriginalPrice(210D);

    //第四步 :将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配
    kieSession.insert(order);

    //第五步: 激活规则引擎,如果规则匹配成功则执行规则
    kieSession.fireAllRules();
    //第六步: 闭会话
    kieSession.dispose();

   
    System.out.println("优惠前原始价格:" + order.getOriginalPrice() +
                       ",优惠后价格:" + order.getRealPrice());
}
springboot集成Drools

第一步 :添加maven依赖

<!--drools规则引擎-->
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>7.6.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.6.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-templates</artifactId>
    <version>7.6.0.Final</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-api</artifactId>
    <version>7.6.0.Final</version>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-spring</artifactId>
    <version>7.6.0.Final</version>
</dependency>

第二步 :创建Drools核心配置, 对应的是快速入门中的(xml文件+第五步单元测试中可复用的代码)

@Configuration
public class DroolsConfig
{
    //规则文件的位置
    private static final String RULES_PATH = "rules/";   
    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException
    {
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        for (Resource file : getRuleFiles())
        {
            kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH
                    + file.getFilename(), "UTF-8"));
        }
        return kieFileSystem;
    }
 
    private Resource[] getRuleFiles() throws IOException
    {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources("classpath*:" + RULES_PATH
                + "**/*.*");
    }
 
    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException
    {
        final KieRepository kieRepository = getKieServices().getRepository();
        //设置时间格式
        System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
        kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        return getKieServices().newKieContainer(
                kieRepository.getDefaultReleaseId());
    }
 
    private KieServices getKieServices()
    {
        return KieServices.Factory.get();
    }
 
    @Bean
    @ConditionalOnMissingBean(KieBase.class)
    public KieBase kieBase() throws IOException
    {
        return kieContainer().getKieBase();
    }
 
    @Bean
    @ConditionalOnMissingBean(KieSession.class)
    public KieSession kieSession() throws IOException
    {
        return kieContainer().newKieSession();
    }
 
 @Bean @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
    public KModuleBeanFactoryPostProcessor kiePostProcessor()
    {
        return new KModuleBeanFactoryPostProcessor();
    }
}

第三步 :创建实体类

package com.itheima.drools.entity;
/**
 * 订单
 */
public class Order {
    private Double originalPrice;//订单原始价格,即优惠前价格
    private Double realPrice;//订单真实价格,即优惠后价格
    public String toString() {
        return "Order{" +
                "originalPrice=" + originalPrice +
                ", realPrice=" + realPrice +
                '}';
    }
    public Double getOriginalPrice() {
        return originalPrice;
    }
    public void setOriginalPrice(Double originalPrice) {
        this.originalPrice = originalPrice;
    }
    public Double getRealPrice() {
        return realPrice;
    }
    public void setRealPrice(Double realPrice) {
        this.realPrice = realPrice;
    }
}

第四步 :创建规则文件(xxx.drl)

//图书优惠规则
package rules
import com.itheima.drools.entity.Order

//规则一:所购图书总价在100元以下的没有优惠
rule "book_discount_1"   //需要唯一
    when   //后面跟条件
        $order:Order(originalPrice < 100)
    then  //符合上面条件则执行后面操作
        $order.setRealPrice($order.getOriginalPrice());
        System.out.println("成功匹配到规则一:所购图书总价在100元以下的没有优惠");
end  //一个规则结束

//规则二:所购图书总价在100到200元的优惠20元
rule "book_discount_2"
    when
        $order:Order(originalPrice < 200 && originalPrice >= 100)
    then
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("成功匹配到规则二:所购图书总价在100到200元的优惠20元");
end

//规则三:所购图书总价在200到300元的优惠50元
rule "book_discount_3"
    when
        $order:Order(originalPrice <= 300 && originalPrice >= 200)
    then
        $order.setRealPrice($order.getOriginalPrice() - 50);
        System.out.println("成功匹配到规则三:所购图书总价在200到300元的优惠50元");
end

//规则四:所购图书总价在300元以上的优惠100元
rule "book_discount_4"
    when
        $order:Order(originalPrice > 300)
    then
        $order.setRealPrice($order.getOriginalPrice() - 100);
        System.out.println("成功匹配到规则四:所购图书总价在300元以上的优惠100元");
end

第五步:编写Service层

@Service
public class DroolsServicesImpl {
    @Autowired
    private KieBase kieBase;


    public Order droolsTest(Order order){
        KieSession session = kieBase.newKieSession();
        session.insert(order);
        session.fireAllRules();
        session.dispose();
        return order;
    }
}
Drools语法 :
rule "book_discount_1"
activation-group "A"    //activation-group :分组,当条件在相同组中都被满足, 也只会执行一个.这里会输出A组中的规则一和B组中的规则3(先头后尾的选择方式)
salience 1    //优先级设置,数字越高,则优先级越高, 和activation-group配合,可以提高尾部的优先级, 从而控制优先执行尾部的判断,而不是头部
    when
        $s:Order(originalPrice < 100)
    then
        $s.setRealPrice($s.getOriginalPrice());
        System.out.println("成功匹配到规则一:所购图书总价在100元以下的没有优惠");
end

//规则二
rule "book_discount_2"
activation-group "A"
    when
        $s:Order(originalPrice < 100)
    then
        $s.setRealPrice($s.getOriginalPrice() - 20);
        System.out.println("成功匹配到规则二:所购图书总价在100到200元的优惠20元");
end

//规则三
rule "book_discount_3"
activation-group "B"
    when
        $s:Order(originalPrice < 100)
    then
        $s.setRealPrice($s.getOriginalPrice() - 50);
        System.out.println("成功匹配到规则三:所购图书总价在200到300元的优惠50元");
end


rule "book_discount_4"
    when
        $s:Order(originalPrice > 300)
    then
        $s.setRealPrice($s.getOriginalPrice() - 100);
        System.out.println("成功匹配到规则四:所购图书总价在300元以上的优惠100元");
end

activation-group “A”

  • activation-group :分组,当条件在相同组中都被满足, 也只会执行一个.这里会输出A组中的规则一和B组中的规则3(先头后尾的选择方式)

salience 1

  • 优先级设置,数字越高,则优先级越高, 和activation-group配合,可以提高尾部的优先级, 从而控制优先执行尾部的判断,而不是头部

no-loop true

lock-on-active true

insert

update

retract | delete

问题 :

当多份规则文件如何加载?

目前业务使用的方式是将规则文件变为字符串的方式存在数据库中, 每次程序执行就将所有规则文件加载一遍,加载到KieBase中.(这里就需要注意关于规则文件的热加载问题)

关于热加载遇到的问题记录 :

当使用drools进行规则判断时, 如果使用的KieSession对象是全局对象(就是应用启动时就创建的KieSession对象), 当使用session.insert(object)进行规则判断时(这里是假代码, session是KieSession这个对象, object是要进行规则判断的);会将object对象缓存到session中, 如果判断成功还好, 判断失败的话.会在下次判断成功时会被一起通过.所以这里一定要注意写个session.delete(FactHandle);将缓存删除.

[注 :这里的FactHandle可以通过上面的session.insert(object)的返回值获得]

热加载使用到的api

查询指定的kieBase对象中存在的规则条例名称及包名


for (KiePackage kiePackage : kieBase.getKiePackages();) {
 for (Rule rule : kiePackage.getRules()) {
     log.info("rule name {}",rule.getName());
     log.info("rule package {}",rule.getPackageName());
 }
}

加载字符串规则文件 :

KieHelper hel = new KieHelper();
hel.addContent(drl,Resource.DRL); //将规则文件放入其中并设置文件类型
Results res = kieHelper.verify();
// Results.getMessage()方法可以获得返回信息来判断是否添加成功
KieSession kie = kieHelper.build().newKieSession(); //获得加载了规则文件的session

向已经创建的kieSession对象中添加/修改/删除规则文件 :

// 先将要添加的文件放入Resource对象中
Resource resource = ResourceFactory.newFileResource(drl);
// 新增或者更新
KnowledgeBuilder kBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kBuilder.add(resource, ResourceType.DRL);
// 创建KieContainer对象(我这边使用的是工具类)
KieContainer con = KieSessionUtil.getKieContainer();
InternalKnowledgeBase knowledgeBase = (InternalKnowledgeBase) con.getKieBase(kieBaseName);
//旧的同package名、同rule名会覆盖,不同package名同rule名不会覆盖;即不同package可以同rule名。
knowledgeBase.addPackages(kBuilder.getKnowledgePackages());
//删除
//knowledgeBase.removeKiePackage("packageName");
//knowledgeBase.removeObjectsGeneratedFromResource(resource);

结束语 :
后续还会增加cloudevent这个技术的笔记.后续如果有什么问题,有什么不足的地方,或我的笔记中有什么错误;欢迎各位大佬能提点几句. 万分感谢.如果有什么你们觉得好用的技术和框架, 如果可以的话能否在评论区中留下言. 后续我整理好也会将笔记发布上来. 希望大家能互相学习.也感谢无偿提供知识的大佬们.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值