规则引擎 Easy rule 使用总结

颜均
2023-12-01

    easy Rule就是一个轻量级的规则引擎,它可以直接注入spring bean类,可以查看github上对它的介绍:GitHub - j-easy/easy-rules: The simple, stupid rules engine for Java

      easy rules是一个简单而强大的java规则引擎,它有以下特性:

  • 轻量级框架
  • 学习成本低
  • 基于POJO 为定义业务引擎提供有用的抽象和简便的应用
  • 从原始的规则组合成复杂的规则

它主要包括几个主要的类或接口:Rule,RulesEngine,RuleListener,Facts

还有几个主要的注解:@Action,@Condition,@Fact,@Priority,@Rule

使用easy rules有两种方式:

  1. 实现Rule接口,并实现其evaluate和execute方法。
  2. 使用@Rule注解修饰POJO

目录

EasyRule源码

几种创建规则的方式

使用easy Rule实战


EasyRule源码

查看下源码:

public interface Rule extends Comparable<Rule> {

    /**
     * 默认的规则名称
     */
    String DEFAULT_NAME = "rule";

    /**
     * 默认的规则描述
     */
    String DEFAULT_DESCRIPTION = "description";

    /**
     * 默认的规则优先级
     */
    int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;

    getter and setter...

    /**
     * 规则引擎判断条件
     * 如果提供的facts被应用到规则上返回true,否则返回false
     */
    boolean evaluate(Facts facts);

    /**
     * 规则引擎判断条件返回true后,执行此方法
     */
    void execute(Facts facts) throws Exception;

}

由上面代码可以知道:一个规则由名称、描述、优先级三个属性和判断、执行两个方法组成,实现Rule接口,和使用@Rule,@Condition,@Action,@Priority,@Fact注解的效果是一样的。
 

public interface RulesEngine {

    /**
     * 返回规则引擎的参数
     */
    RulesEngineParameters getParameters();

    /**
     * 返回已注册的规则监听器的列表
     */
    List<RuleListener> getRuleListeners();

    /**
     * 在给定的因素上开启所有已注册的规则
     */
    void fire(Rules rules, Facts facts);

    /**
     * 检查规则和因素是否符合
     */
    Map<Rule, Boolean> check(Rules rules, Facts facts);
}

 RulesEngine负责检查和开启规则,同时可以得到规则引擎的参数和规则监听器列表

public interface RuleListener {

    /**
     * 规则条件判断之前的触发器
     */
    boolean beforeEvaluate(Rule rule, Facts facts);

    /**
     * 规则条件判断之后的触发器
     */
    void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult);

    /**
     * 规则执行之前的触发器
     */
    void beforeExecute(Rule rule, Facts facts);

    /**
     * 规则执行成功之后的触发器
     */
    void onSuccess(Rule rule, Facts facts);

    /**
     * 规则执行失败之后的触发器
     */
    void onFailure(Rule rule, Facts facts, Exception exception);

}

RuleListener  规则执行过程的监听器,可以监听规则是否触发,规则执行的结果

package org.jeasy.rules.api;

public interface RulesEngineListener {
   //该方法用于判断规则 参数。在RuleListener之前执行
    void beforeEvaluate(org.jeasy.rules.api.Rules rules, org.jeasy.rules.api.Facts facts);
    // 规则执行成功后。在RuleListener之后执行
    void afterExecute(org.jeasy.rules.api.Rules rules, org.jeasy.rules.api.Facts facts);
}
public class Facts implements Iterable<Map.Entry<String, Object>> {

    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);
        }
    }

    /**
     * 通过name得到因素
     */
    @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;
    }
    

    /**
     * 以map形式返回因素
     */
    public Map<String, Object> asMap() {
        Map<String, Object> map = new HashMap<>();
        for (Fact<?> fact : facts) {
            map.put(fact.getName(), fact.getValue());
        }
        return map;
    }
    ...
}

Facts就是一个hashmap,通过注解@Fact(String value),其中的value是map的key,可以拿到Facts中的value。

大致的步骤:

  1. 创建各种规则,即rule.可以通过 pojo 或 RuleBuilder API 两种方式创建。
  2. 创建规则容器Rules rules = new Rules();
  3. 注册创建的规则rules.add(rule);
  4. 创建流,即facts.流控制规则的参数输入.Facts facts = new Facts();facts.add("参数名", 参数值);
  5. 创建引擎,即RulesEngine。
  6. 由引擎把facts和rules结合进行处理,RulesEngine.fire(rules, facts);

几种创建规则的方式

  • 流式创建规则
Rule weatherRule = new RuleBuilder()
        .name("weather rule")
        .description("if it rains then take an umbrella")
        .when(facts -> facts.get("rain").equals(true))
        .then(facts -> System.out.println("It rains, take an umbrella!"))
        .build();
Rule weatherRule = new MVELRule()
        .name("weather rule")
        .description("if it rains then take an umbrella")
        .when("rain == true")
        .then("System.out.println(\"It rains, take an umbrella!\");");

  • 注解形式
@Rule(name = "weather rule", description = "if it rains then take an umbrella")
@Rule
public class FizzRule {

    @Condition
    public boolean isFizz(@Fact("number") Integer number) {
        return number % 5 == 0;
    }

    @Action
    public void printFizz() {
        System.out.print("fizz");
    }

    @Priority
    public int getPriority() {
        return 1;
    }
}
  •  创建规则文件
weather-rule.yml文件

name: "weather rule"
description: "if it rains then take an umbrella"
condition: "rain == true"
actions:
  - "System.out.println(\"It rains, take an umbrella!\");"

解析yml规则文件

MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));
  • 注解加流式形式创建规则
public class HighTemperatureCondition implements Condition {

    @Override
    public boolean evaluate(Facts facts) {
        Integer temperature = facts.get("temperature");
        return temperature > 25;
    }

    static HighTemperatureCondition itIsHot() {
        return new HighTemperatureCondition();
    }

}

 
public class DecreaseTemperatureAction implements Action {

    @Override
    public void execute(Facts facts) throws Exception {
        System.out.println("It is hot! cooling air..");
        Integer temperature = facts.get("temperature");
        facts.put("temperature", temperature - 1);
    }

    static DecreaseTemperatureAction decreaseTemperature() {
        return new DecreaseTemperatureAction();
    }
}

 
Rule airConditioningRule = new RuleBuilder()
        .name("air conditioning rule")
        .when(itIsHot())
        .then(decreaseTemperature())
        .build();

 加载规则到规则引擎,并注入参数触发规则

Facts facts = new Facts();
facts.put("rain", true);

// define rules
Rule weatherRule = ...
Rules rules = new Rules();
rules.register(weatherRule);

// fire rules on known facts
RulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(rules, facts);

使用easy Rule实战

下面给出自己的一个使用easy rule规则引擎调用业务类的例子:

1:封装 规则引擎

@Component
public class RuleUtil {
    DefaultRulesEngine rulesEngine = null;
    Facts facts = null;
    Rules rules = null;

    public RulesEngine getRuleEngine(){return rulesEngine;}

    public Rules getRules() {
        return rules;
    }

    public void setRules(Rules rules) {
        this.rules = rules;
    }

    public Facts getFacts() {
        return facts;
    }

    public void setFacts(Facts facts) {
        this.facts = facts;
    }

 public void addFact(String name,Object vule){
        facts.put(name,vule);
    }


    public void registRule(CvbRule cvbRule){
        MVELRule rule = creatRule(cvbRule);
        rules.register(rule);
    }

    public void deleteRule(CvbRule cvbRule) {
        String ruleName = cvbRule.getCvbRuleName();
        rules.unregister(ruleName);
    }

    public void putRule(Rules rules1,CvbRule cvbRule){
        MVELRule rule = creatRule(cvbRule);
        rules1.register(rule);
    }


   /**
     * 初始化基本参数信息
     */
    public void init() {
        RulesEngineParameters parameters = new RulesEngineParameters();
        //匹配第一个后面就不在匹配
        //parameters.skipOnFirstAppliedRule(true);
        parameters.skipOnFirstAppliedRule(false);
        parameters.skipOnFirstFailedRule(false);
        parameters.skipOnFirstFailedRule(false);
        parameters.skipOnFirstNonTriggeredRule(false);

        rulesEngine = new DefaultRulesEngine(parameters);
        rulesEngine.registerRuleListener(myRuleListener);
//        rulesEngine.registerRulesEngineListener(myRuleEngineListener);

        facts = new Facts();
        rules = new Rules();
    }

    /**
     * 触发指定规则,并赋予参数
     * @param rules1
     * @param facts1
     */
    public void triggerRule(Rules rules1,Facts facts1) {
        rulesEngine.fire(rules1, facts1);
    }

    /**
     * 触发规则引擎所有规则
     */
    public void triggerAllRule(){rulesEngine.fire(rules, facts);}

    /**
     * 流式创建规则
     * @param cvbRule
     * @return
     */
    public MVELRule creatRule(CvbRule cvbRule) {
        MVELRule rule = new MVELRule().name(cvbRule.getCvbRuleName())
                .description(cvbRule.getCvbRuleDesc())
                .priority(RandomUtils.generateRandomNumber(5))
                .when("mqService.doRuleConditions(conditionList,tmpParam)")
                .then("mqService.doRuleOperation(cvbOperation,tmpParam)");
        return rule;
    }

}

可以看到上面我是直接注入的参数是业务类,Spring管理的业务类,并且直接调用业务类方法,并且传入参数。

具体的注入参数,以及业务类:

这里我写了一个线程池,通过阻塞队列来并发执行规则:

@Slf4j
@Component
public class Consumer implements Runnable {
    @Autowired
    private RuleUtil ruleUtil;
    @Autowired
    private MqService mqService;

    private BlockingQueue queue;

    public void setQueue(BlockingQueue queue) {
        this.queue = queue;
    }

    private static AtomicLong count = new AtomicLong(0);

     @Override
    public void run() {
        Lock lock = new ReentrantLock();
        try {
            boolean rs = lock.tryLock(10, TimeUnit.SECONDS);
            if (rs) {
                TmpParam message =     JSONObject.parseObject(JSONObject.toJSONString(queue.poll(2, TimeUnit.SECONDS)), TmpParam.class);
                log.info("----消费者拿到消息:{}----", message);

                if (message.getData() != null) {
                    Long num = count.getAndIncrement();
                    Facts facts = new Facts();

                    //直接注入参数  在监听的地方根据规则注入 规则的条件 规则的操作
                    facts.put("tmpParam", message);
                    facts.put("mqService", mqService);
                    ruleUtil.setFacts(facts);
                    ruleUtil.triggerAllRule();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            log.info("-----消费者处理完成----");
            lock.unlock();
        }
    }

}

我们通过上面注入的规则监听器去监听规则的执行情况 ,下面是我自己定义的监听类,实现类  RuleListener接口

public class MyRuleListener implements RuleListener {

     @Override
    public boolean beforeEvaluate(Rule rule, Facts facts) {
        log.info("-----{}-----Condition之前的执行----", rule.getName());
        String ruleName = rule.getName();
        //拿到规则信息
   
        //这里是可以对参数进行判断校验的


        return true;
    }

    /**
     * 规则条件判断之后的触发器
     * b表示规则是否触发
     */
    @Override
    public void afterEvaluate(Rule rule, Facts facts, boolean b) {
        log.info("----{} ---Condition执行之后 可以得到规则是否触发----", rule.getName());
        if (b) {
            log.info("----{} 规则被触发-----", rule.getName());

        } else {
            log.info("-----{} 规则没有被触发----", rule.getName());

         
        }
    }

    /**
     * 规则执行之前的触发器
     */
    @Override
    public void beforeExecute(Rule rule, Facts facts) {
        log.info("-----{} ----在执行Action之前执行----", rule.getName());

    }

    /**
     * 规则执行成功之后的触发器
     */
    @Override
    public void onSuccess(Rule rule, Facts facts) {
        log.info("-----{}---- 规则Action执行完并且是成功的---", rule.getName());

        String ruleName = rule.getName();
       

    }

    /**
     * 规则执行失败之后的触发器
     */
    @Override
    public void onFailure(Rule rule, Facts facts, Exception e) {
        log.info("-----{} ----Action执行失败 ----", rule.getName());
        String ruleName = rule.getName();
      
    }
}
public class MyRuleEngineListener implements RulesEngineListener {
    @Override
    public void beforeEvaluate(Rules rules, Facts facts) {
        //先执行
        log.info("----- 该方法用于判断规则 参数。在RuleListener之前执行----");

    }
  
    @Override
    public void afterExecute(Rules rules, Facts facts) {
        log.info("----- 规则执行成功后。在RuleListener之后执行 ----");

    }
}

以上就是使用easy Rule 实现规则引擎,并通过监听接口监听规则使用结果情况,具体其他的用法大家可以参考github 上的源码,也可以定制自己的规则引擎。

 类似资料: