1. 问题引出

现有一个在线申请信用卡的业务场景,用户需要录入个人信息,如下图所示:

通过上图可以看到,用户录入的个人信息包括姓名、性别、年龄、学历、电话、所在公司、职位、月收入、是否有房、是否有车、是否有信用卡等。录入完成后点击申请按钮提交即可。

用户提交申请后,需要在系统的服务端进行用户信息合法性检查(是否有资格申请信用卡),只有通过合法性检查的用户才可以成功申请到信用卡(注意:不同用户有可能申请到的信用卡额度不同)。

检查用户信息合法性的规则如下:

规则编号 名称 描述
1 检查学历与薪水1 如果申请人既没房也没车,同时学历为大专以下,并且月薪少于5000,那么不通过
2 检查学历与薪水2 如果申请人既没房也没车,同时学历为大专或本科,并且月薪少于3000,那么不通过
3 检查学历与薪水3 如果申请人既没房也没车,同时学历为本科以上,并且月薪少于2000,同时之前没有信用卡的,那么不通过
4 检查申请人已有的信用卡数量 如果申请人现有的信用卡数量大于10,那么不通过

用户信息合法性检查通过后,还需要根据如下信用卡发放规则确定用户所办信用卡的额度:

规则编号 名称 描述
1 规则1 如果申请人有房有车,或者月收入在20000以上,那么发放的信用卡额度为15000
2 规则2 如果申请人没房没车,但月收入在10000~20000之间,那么发放的信用卡额度为6000
3 规则3 如果申请人没房没车,月收入在10000以下,那么发放的信用卡额度为3000
4 规则4 如果申请人有房没车或者没房但有车,月收入在10000以下,那么发放的信用卡额度为5000
5 规则5 如果申请人有房没车或者是没房但有车,月收入在10000~20000之间,那么发放的信用卡额度为8000

思考:如何实现上面的业务逻辑呢?

我们最容易想到的就是使用分支判断(if else)来实现,例如通过如下代码来检查用户信息合法性:

//此处为伪代码//检查用户信息合法性,返回true表示检查通过,返回false表示检查不通过
public boolean checkUser(User user){//如果申请人既没房也没车,同时学历为大专以下,并且月薪少于5000,那么不通过if(user.getHouse() == null && user.getcar() == null && user.getEducation().equals("大专以下") && user.getSalary < 5000){return false;}//如果申请人既没房也没车,同时学历为大专或本科,并且月薪少于3000,那么不通过else if(user.getHouse() == null && user.getcar() == null && user.getEducation().equals("大专或本科") && user.getSalary < 3000){return false;}//如果申请人既没房也没车,同时学历为本科以上,并且月薪少于2000,同时之前没有信用卡的,那么不通过else if(user.getHouse() == null && user.getcar() == null && user.getEducation().equals("本科以上") && user.getSalary < 2000 && user.getHasCreditCard() == false){return false;}//如果申请人现有的信用卡数量大于10,那么不通过else if(user.getCreditCardCount() > 10){return false;}return true;
}

如果用户信息合法性检查通过后,还需要通过如下代码确定用户所办信用卡的额度:

//此处为伪代码//根据用户输入信息确定信用卡额度
public Integer determineCreditCardLimit(User user){//如果申请人有房有车,或者月收入在20000以上,那么发放的信用卡额度为15000if((user.getHouse() != null && user.getcar() != null) || user.getSalary() > 20000){return 15000;}//如果申请人没房没车,并且月收入在10000到20000之间,那么发放的信用卡额度为6000else if(user.getHouse() == null && user.getcar() == null&& user.getSalary() > 10000 && user.getSalary() < 20000){return 6000;}//如果申请人没房没车,并且月收入在10000以下,那么发放的信用卡额度为3000else if(user.getHouse() == null && user.getcar() == null&& user.getSalary() < 10000){return 3000;}//如果申请人有房没车或者没房但有车,并且月收入在10000以下,那么发放的信用卡额度为5000else if((((user.getHouse() != null && user.getcar() == null) || (user.getHouse() == null && user.getcar() != null))&& user.getSalary() < 10000){return 5000;}//如果申请人有房没车或者没房但有车,并且月收入在10000到20000之间,那么发放的信用卡额度为8000else if((((user.getHouse() != null && user.getcar() == null) || (user.getHouse() == null && user.getcar() != null))&& (user.getSalary() > 10000 && user.getSalary() < 20000)){return 8000;}
}

通过上面的伪代码我们可以看到,我们的业务规则是通过Java代码的方式实现的。这种实现方式存在如下问题:

1、硬编码实现业务规则难以维护

2、硬编码实现业务规则难以应对变化

3、业务规则发生变化需要修改代码,重启服务后才能生效

那么面对上面的业务场景,还有什么好的实现方式吗?

答案是规则引擎

2. 规则引擎概述

2.1 什么是规则引擎

规则引擎,全称为业务规则管理系统,英文名为BRMS(即Business Rule Management System)。规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理。

需要注意的是规则引擎并不是一个具体的技术框架,而是指的一类系统,即业务规则管理系统。目前市面上具体的规则引擎产品有:drools、VisualRules、iLog等。

规则引擎实现了将业务决策从应用程序代码中分离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策。规则引擎其实就是一个输入输出平台。

上面的申请信用卡业务场景使用规则引擎后效果如下:

系统中引入规则引擎后,业务规则不再以程序代码的形式驻留在系统中,取而代之的是处理规则的规则引擎,业务规则存储在规则库中,完全独立于程序。业务人员可以像管理数据一样对业务规则进行管理,比如查询、添加、更新、统计、提交业务规则等。业务规则被加载到规则引擎中供应用系统调用。

2.2 使用规则引擎的优势

使用规则引擎的优势如下:

1、业务规则与系统代码分离,实现业务规则的集中管理

2、在不重启服务的情况下可随时对业务规则进行扩展和维护

3、可以动态修改业务规则,从而快速响应需求变更

4、规则引擎是相对独立的,只关心业务规则,使得业务分析人员也可以参与编辑、维护系统的业务规则

5、减少了硬编码业务规则的成本和风险

6、使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单

2.3 规则引擎应用场景

对于一些存在比较复杂的业务规则并且业务规则会频繁变动的系统比较适合使用规则引擎,如下:

1、风险控制系统----风险贷款、风险评估

2、反欺诈项目----银行贷款、征信验证

3、决策平台系统----财务计算

4、促销平台系统----满减、打折、加价购

2.4 Drools介绍

drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。

drools官网地址:https://drools.org/

drools源码下载地址:https://github.com/kiegroup/drools

在项目中使用drools时,即可以单独使用也可以整合spring使用。如果单独使用只需要导入如下maven坐标即可:

<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.6.0.Final</version>
</dependency>

如果我们使用IDEA开发drools应用,IDEA中已经集成了drools插件。如果使用eclipse开发drools应用还需要单独安装drools插件。

drools API开发步骤如下:

3. Drools入门案例

本小节通过一个Drools入门案例来让大家初步了解Drools的使用方式、对Drools有一个整体概念。

3.1 业务场景说明

业务场景:消费者在图书商城购买图书,下单后需要在支付页面显示订单优惠后的价格。具体优惠规则如下:

规则编号 规则名称 描述
1 规则一 所购图书总价在100元以下的没有优惠
2 规则二 所购图书总价在100到200元的优惠20元
3 规则三 所购图书总价在200到300元的优惠50元
4 规则四 所购图书总价在300元以上的优惠100元

现在需要根据上面的规则计算优惠后的价格。

3.2 开发实现

第一步:创建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>

第二步:根据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.example.demo.entity;/*** 订单* @author admin*/
public class Order {/*** 订单原始价格,即优惠前价格*/private Double originalPrice;/*** 订单真实价格,即优惠后价格*/private Double realPrice;@Overridepublic 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 (drl文件名称可以随意)

//图书优惠规则
package book.discount
import com.example.demo.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

第五步:编写单元测试

package com.example.demo.test;import com.example.demo.entity.Order;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;public class Test {@org.junit.Testpublic void test1(){KieServices kieServices = KieServices.Factory.get();KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();//会话对象,用于和规则引擎交互(这里的参数取得是xml中配置的name,否则引擎不生效)KieSession kieSession = kieClasspathContainer.newKieSession("ksession-rule");//构造订单对象,设置原始价格,由规则引擎根据优惠规则计算优惠后的价格Order order = new Order();order.setOriginalPrice(210D);//将数据提供给规则引擎,规则引擎会根据提供的数据进行规则匹配kieSession.insert(order);//激活规则引擎,如果规则匹配成功则执行规则kieSession.fireAllRules();//关闭会话kieSession.dispose();System.out.println("优惠前原始价格:" + order.getOriginalPrice() +",优惠后价格:" + order.getRealPrice());}}

通过上面的入门案例我们可以发现,使用drools规则引擎主要工作就是编写规则文件,在规则文件中定义跟业务相关的业务规则,例如本案例定义的就是图书优惠规则。规则定义好后就需要调用drools提供的API将数据提供给规则引擎进行规则模式匹配,规则引擎会执行匹配成功的规则并将计算的结果返回给我们。

可能大家会有疑问,就是我们虽然没有在代码中编写规则的判断逻辑,但是我们还是在规则文件中编写了业务规则,这跟在代码中编写规则有什么本质的区别呢?

我们前面其实已经提到,使用规则引擎时业务规则可以做到动态管理。业务人员可以像管理数据一样对业务规则进行管理,比如查询、添加、更新、统计、提交业务规则等。这样就可以做到在不重启服务的情况下调整业务规则。

3.3 小结

3.3.1 规则引擎构成

drools规则引擎由以下三部分构成:

  • Working Memory(工作内存)
  • Rule Base(规则库)
  • Inference Engine(推理引擎)

其中Inference Engine(推理引擎)又包括:

  • Pattern Matcher(匹配器)
  • Agenda(议程)
  • Execution Engine(执行引擎)

3.3.2 相关概念说明

Working Memory:工作内存,drools规则引擎会从Working Memory中获取数据并和规则文件中定义的规则进行模式匹配,所以我们开发的应用程序只需要将我们的数据插入到Working Memory中即可,例如本案例中我们调用kieSession.insert(order)就是将order对象插入到了工作内存中。

Fact:事实,是指在drools 规则应用当中,将一个普通的JavaBean插入到Working Memory后的对象就是Fact对象,例如本案例中的Order对象就属于Fact对象。Fact对象是我们的应用和规则引擎进行数据交互的桥梁或通道。

Rule Base:规则库,我们在规则文件中定义的规则都会被加载到规则库中。

Pattern Matcher:匹配器,将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,匹配成功的规则将被激活并放入Agenda中。

Agenda:议程,用于存放通过匹配器进行模式匹配后被激活的规则。

Execution Engine:执行引擎,执行Agenda中被激活的规则。

Drools规则引擎入门小demo相关推荐

  1. drools 6.5 -规则引擎入门

    本文结构: 1. Drools 2. Drools 规则引擎原理 3. 入门例子 1. Drools Drools 具有一个易于访问企业策略.易于调整以及易于管理的开源业务 规则引擎,符合业内标准,速 ...

  2. 【Drools规则引擎】基础入门案例五(Kie-Server+WorkBench)

    [Drools规则引擎]基础入门案例五(Kie-Server+WorkBench) 介绍 搭建WorkBench 搭建Kie-Server 使用WorkBench 访问登入 客户端测试 介绍 java ...

  3. Drools规则引擎之入门案例(一)

    1.问题引出 一个在线申请信用卡的业务场景,用户需要录入个人信息,通过上图可以看到,用户录入的个人信息包括姓名.性别.年龄.学历.电话.所在公司.职位.月收入.是否有房.是否有车.是否有信用卡等.录入 ...

  4. 详解:Drools规则引擎探究

    引入 ▐ 问题引入 天猫奢品业务方为了吸引更多的新客,和提高会员的活跃度,做了一期活动,通过购买天猫奢品频道内的任意商品就赠送特殊积分,积分可以直接兑换限量的奢品商品.假如业务方给的规则如下: 主刃同 ...

  5. 使用 Drools 规则引擎实现业务逻辑,可调试drl文件

    http://www.srcsky.com/tech/arts/389.html 代码下载http://download.csdn.net/detail/zhy011525/2462313 使用 Dr ...

  6. drools规则引擎的基本使用和原理介绍

    理论基石 借用:<drools规则引擎技术指南>来说, drools是: 开源项目,规则引擎技术,规则语法形成的规则文件,可以存在数据库等,通过drools包提供的接口,调用生成对应的结果 ...

  7. Drools 规则引擎

    官网:https://www.drools.org/ 累了听听歌:http://www.hy57.com/p/158102.html 1. 快速度入门 1. 导入依赖 <dependencies ...

  8. 黑马Drools学习笔记(一)——Drools规则引擎示例概述以及SpringBoot整合示例

    文章目录 1. 问题引出 2. 规则引擎概述 2.1 什么是规则引擎 2.2 使用规则引擎的优势 2.3 规则引擎应用场景 2.4 Drools介绍 3. Drools入门案例 3.1 业务场景说明 ...

  9. drools规则引擎因为内存泄露导致的内存溢出

    进入这个问题之前,先了解一下drools: 在很多行业应用中比如银行.保险领域,业务规则往往非常复杂,并且规则处于不断更新变化中,而现有很多系统做法基本上都是将业务规则绑定在程序代码中. 主要存在的问 ...

  10. SpringBoot整合Drools规则引擎动态生成业务规则

    最近的项目中,使用的是flowable工作流来处理业务流程,但是在业务规则的配置中,是在代码中直接固定写死的,领导说这样不好,需要规则可以动态变化,可以通过页面去动态配置改变,所以就花了几天时间去研究 ...

最新文章

  1. html5游戏加载动画,HTML5 战场3游戏的加载指示动画
  2. 【Android 逆向】应用安装目录 ( Android 应用的默认安装目录 | 查找 Android 应用的安装目录 | 查询当前正在运行的应用包名 | 根据包名查询应用安装路径 )
  3. python sql in list
  4. LInux之建立文件和目录
  5. octotree插件 --- 将 Github 项目代码以树形格式展示
  6. WINDBG常用方法
  7. dns服务与配置管理,一机多站
  8. python制图一元迭代函数_Python中 生成器、迭代器、闭包、装饰器、元类实例分析...
  9. 解决修改jsp代码之后,浏览器刷新页面却没有变化
  10. 搜索引擎整体结构图以及描述
  11. python 卡方分布函数_推断统计分析(二):python验证三大抽样分布
  12. 微信公众号授权成功重定向后点击返回最上一层时显示空白页
  13. android studio 自定义皮肤
  14. influxdb java api使用_InfluxDB使用HTTP的API查询数据
  15. Python-docx实战:同事要我帮忙补写178份日报!别吧
  16. 【Transformers】第 3 章:Transformers剖析
  17. Bug: Return value of putIfAbsent is ignored, but list is reused
  18. Android 读取csv格式数据文件
  19. 尝试搭建本地语音转文字系统[1]
  20. python广义极值_广义极值分布的数据拟合

热门文章

  1. 为什么软件需要数字签名?代码签名证书的作用
  2. idea2021设置代码字体大小
  3. 桌面软件开发框架大赏
  4. 推荐一个开源好用的ER图、流程图作图软件-draw.io
  5. android 自定义唤醒词,星星1号语音升级 中兴语音助手实现可自定义唤醒词
  6. win10默认壁纸_小白个人系统安装美化(二)win10系统美化设置篇
  7. 视频传输的方式有哪些?
  8. P2525 Uim的情人节礼物·其之壱 prev_permutaion
  9. python中unicode编码表_python中的unicode编码
  10. 数值分析复习(六)——常微分方程数值解法