当前位置: 代码迷 >> 综合 >> Java规则引擎easy rules
  详细解决方案

Java规则引擎easy rules

热度:41   发布时间:2023-12-22 01:55:34.0

场景

简单点描述,有点策略模式的味道,所以可以处理if…else…语句;

其核心内容还是在规则引擎,所以和Drools规则类似,目前支持MVEL和SpEL表达式,配置外置;

最后支持各种规则的组合,支持OR和AND等多种规则组合模式。

1、支持facts作为参数判断,解放if…else…语句;

3、支持规则文件外置,释放研发生产力;

2、支持规则组合,实现多业务规则链路执行,短路执行。

功能

  • 轻量级框架,基于API方式
  • 支持基于POJO和注解方式开发
  • 支持创建组合规则
  • 支持使用表达式语言(MVEL/SpEL)

名词描述

  1. name:规则名称,唯一性
  2. description:说明描述
  3. priority:优先级
  4. Facts:事实
  5. Conditions:条件
  6. Actions:执行动作

案例

背景

定义一个业务订单的场景,根据订单类型,执行对应的业务操作。假设,订单类型为两种:普通类型、折扣类型。

普通类型订单:打印日志处理
折扣类型订单:执行折扣计算方法

maven依赖

 <dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-core</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-mvel</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-spel</artifactId><version>4.0.0</version></dependency>

订单对象

@Data
public class BizOrder {
    
?/*** 商品*/private String goods;
?/*** 售价*/private BigDecimal amount;
?/*** 订单类型 1、普通类型 2、折扣类型*/private Integer type;
?/*** 折扣*/private BigDecimal discount;
}

折扣计算

@Component
@Slf4j
public class OrderService {
    
?/*** 折扣处理方法* @param param 参数*/public static void doDiscountAction(Object param) {
    if (param instanceof BizOrder) {
    BizOrder order = ((BizOrder) param);log.error("商品goods:{},折后amount:{}", order.getGoods(), order.getAmount().multiply(order.getDiscount()));}}
}

实现方式

1、基于表达式

步骤

定义事实数据

//定义事实数据
Facts facts = new Facts();
facts.put("type", order.getType());

设置了订单类型作为参数。
源码分析

public class Facts implements Iterable<Fact<?>> {
    
?private final Set<Fact<?>> facts = new HashSet<>();
?public <T> void put(String name, T value) {
    Objects.requireNonNull(name, "fact name must not be null");Objects.requireNonNull(value, "fact value must not be null");Fact<?> retrievedFact = getFact(name);if (retrievedFact != null) {
    remove(retrievedFact);}add(new Fact<>(name, value));}public <T> void add(Fact<T> fact) {
    Objects.requireNonNull(fact, "fact must not be null");Fact<?> retrievedFact = getFact(fact.getName());if (retrievedFact != null) {
    remove(retrievedFact);}facts.add(fact);}
?public void remove(String factName) {
    Objects.requireNonNull(factName, "fact name must not be null");Fact<?> fact = getFact(factName);if (fact != null) {
    remove(fact);}}
?public <T> void remove(Fact<T> fact) {
    Objects.requireNonNull(fact, "fact must not be null");facts.remove(fact);}
?@SuppressWarnings("unchecked")public <T> T get(String factName) {
    Objects.requireNonNull(factName, "fact name must not be null");Fact<?> fact = getFact(factName);if (fact != null) {
    return (T) fact.getValue();}return null;}public Fact<?> getFact(String factName) {
    Objects.requireNonNull(factName, "fact name must not be null");return facts.stream().filter(fact -> fact.getName().equals(factName)).findFirst().orElse(null);}
?public Map<String, Object> asMap() {
    Map<String, Object> map = new HashMap<>();for (Fact<?> fact : facts) {
    map.put(fact.getName(), fact.getValue());}return map;}
?@Overridepublic Iterator<Fact<?>> iterator() {
    return facts.iterator();}
?public void clear() {
    facts.clear();}
?@Overridepublic String toString() {
    Iterator<Fact<?>> iterator = facts.iterator();StringBuilder stringBuilder = new StringBuilder("[");while (iterator.hasNext()) {
    stringBuilder.append(iterator.next().toString());if (iterator.hasNext()) {
    stringBuilder.append(",");}}stringBuilder.append("]");return stringBuilder.toString();}
}

实现了Iterable接口,含有一个Set集合的容器。

表达式分别定义普通类型、折扣类型规则Rule。

Rule ordinaryRule = new RuleBuilder().name("ordinary_order_rule").description("普通订单类型").when(vo -> vo.get("type").equals(1)).then(vo -> log.info("这是一个普通订单")).build();

源码分析

public interface Rule extends Comparable<Rule> {
    
?String DEFAULT_NAME = "rule";
?String DEFAULT_DESCRIPTION = "description";
?int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;
?default String getName() {
    return DEFAULT_NAME;}
?default String getDescription() {
    return DEFAULT_DESCRIPTION;}
?default int getPriority() {
    return DEFAULT_PRIORITY;}
?boolean evaluate(Facts facts);void execute(Facts facts) throws Exception;

很明显是Comparable的一个子类,方法evaluate()返回boolean是否加载了规则,以及execute()执行规则动作。

注册规则

//注册
Rules rules = new Rules();
rules.register(activationRuleGroup);

启动点火

 //启动点火RulesEngine rulesEngine = new DefaultRulesEngine();rulesEngine.fire(rules, facts);

采用了默认的规则引擎
规则引擎实现类?如下:
在这里插入图片描述

整体代码实现

@AuthIgnore@OperationLog(value = "easy rules 表达式测试")@PostMapping("/stream")@SneakyThrowspublic ResultVo<?> streamTest(@RequestBody BizOrder order) {
    //定义事实数据Facts facts = new Facts();facts.put("type", order.getType());ActivationRuleGroup activationRuleGroup = new ActivationRuleGroup("order_type_rule","订单类型规则");//普通类型Rule ordinaryRule = new RuleBuilder().name("ordinary_order_rule").description("普通订单类型").when(vo -> vo.get("type").equals(1)).then(vo -> log.info("这是一个普通订单")).build();activationRuleGroup.addRule(ordinaryRule);//折扣类型Rule discountRule = new RuleBuilder().name("discount_order_rule").description("折扣订单类型").when(vo -> vo.get("type").equals(2)).then(vo -> {
    log.info("这是一个折扣订单");//执行其他逻辑OrderService.doDiscountAction(order);}).build();activationRuleGroup.addRule(discountRule);//注册Rules rules = new Rules();rules.register(activationRuleGroup);//启动点火RulesEngine rulesEngine = new DefaultRulesEngine();rulesEngine.fire(rules, facts);return ResultVo.success();}

调试

普通类型
普通类型
?折扣类型
日志输出,模拟业务场景

我们也可以从日志中看出规则引擎加载的参数,规则注册,以及facts的赋值,

2、基于注解

分别定义两个规则POJO

普通类型订单

@Rule(name = "ordinary_order_rule", description = "普通订单类型", priority = 1)
@Slf4j
public class OrdinaryOrderRule {
    
?@Conditionpublic boolean when(@Fact("order") BizOrder order) {
    return Objects.equals(order.getType(), 1);}
?@Actionpublic void action(@Fact("order") BizOrder order) {
    log.info("这是一个普通订单,商品goods:{}", order.getGoods());}
}

折扣类型订单

@Rule(name = "discount_order_rule", description = "折扣订单类型",priority =2)
@Slf4j
public class DiscountOrderRule {
    
?@Conditionpublic boolean when(@Fact("order")BizOrder order) {
    return Objects.equals(order.getType(),2);}
?@Action(order = 1)public void action(@Fact("order")BizOrder order) {
    log.info("这是一个折扣订单,商品goods:{}",order.getGoods());}
?@Action(order = 2)public void action2(@Fact("order")BizOrder order) {
    //调用其他业务处理OrderService.doDiscountAction(order);}
}

接口处理

@AuthIgnore@OperationLog(value = "easy rules 注解测试")@PostMapping("/annotation")@SneakyThrowspublic ResultVo<?> annotationTest(@RequestBody BizOrder order) {
    //定义数据Facts facts = new Facts();facts.put("order", order);ActivationRuleGroup activationRuleGroup = new ActivationRuleGroup("order_type_rule","订单类型规则");activationRuleGroup.addRule(new OrdinaryOrderRule());activationRuleGroup.addRule(new DiscountOrderRule());//注册Rules rules = new Rules();rules.register(activationRuleGroup);//启动点火RulesEngine rulesEngine = new DefaultRulesEngine();rulesEngine.fire(rules, facts);return ResultVo.success();}

3、基于配置文件

可以是json(本文),也可以是yml,当然也可以结合Apollo。
定义json文件,biz_order_rule.json

[{
    "name":"order_type_rule","description":"订单类型规则引擎","priority":1,"compositeRuleType":"ActivationRuleGroup","composingRules":[{
    "name":"ordinary_order_rule","description":"普通订单类型","condition":"param.getType().equals(1)","priority":1,"actions":["System.out.println(\"这是一个普通订单\")"]},{
    "name":"discount_order_rule","description":"折扣类型规则","condition":"param.getType().equals(2)","priority":2,"actions":["OrderService.doDiscountAction(param)"]}]}
]

构建rules配置

@Component
public class ConfigRules {
    
?/*** 构建rules配置*/public Rules fetchConfigRules() throws Exception {
    //JSON 表达式MVELRuleFactory ruleFactory = new MVELRuleFactory(new JsonRuleDefinitionReader());return ruleFactory.createRules(new FileReader(Objects.requireNonNull(SpringBootApplication.class.getClassLoader().getResource("biz_order_rule.json")).getFile()));}
}

API逻辑处理

  @AuthIgnore@OperationLog(value = "easy rules 配置文件测试")@PostMapping("/json")@SneakyThrowspublic ResultVo<?> jsonTest(@RequestBody BizOrder order) {
    //定义数据Facts facts = new Facts();facts.put("param", order);facts.put("OrderService",OrderService.class);//读取配置Rules rules = configRules.fetchConfigRules();BizRule<BizOrder> rule = new BizRule<>();//注册rules.register(rule);//启动点火RulesEngine rulesEngine = new DefaultRulesEngine();rulesEngine.fire(rules, facts);return ResultVo.success();}

参考官方文档:https://github.com/j-easy/easy-rules/wiki

  相关解决方案