EasyRules动态规则实现

EasyRules动态规则实现

之前写了easyRules的基本使用方法,可以说还是非常方便而且操作也不复杂,个人觉得相较于Drools来说是简便了不少。接下来就需要更深入的了解一下规则的动态使用方法。

动态规则

动态规则就是由于业务场景的变化,之前的规则已经不适用现在的业务场景,需要更改相对应的规则,但是又不想每次都去修改代码,例如:某宝做活动,之前是满100块才能减免,但是现在只用满10块就能减免了,按照以前的逻辑,我们需要去一下规则文件里面的比较参数就可以,但是这样就需要重新编译打包才能生效,如果使用动态规则,就是我传一个规则文件,只需要运用这个规则文件,就能达到效果,也不需要重新编译打包,何乐而不为呢!!!

要实现这个效果,可以利用easyRules规则定义的两种方式来实现,ymal文件定义规则和json文件定义规则。

存在的问题

这里先说一下在使用过程中遇到的问题(这几个问题都是在运行代码时发现的,可以结合下面的代码实现来看):

  1. 使用规则文件定义规则,condition和action中只能操作facts中有的对象

    在规则文件中虽然可以在conditon和action中使用java代码来写逻辑,但是无法向规则类(pojo类定义的规则)那样的输入参数,因为规则文件中是没办法设置传入参数,例如,在规则类中的action注解对应的方法中我们存入了facts参数,可以使用facts.get()方法获取内容,但是在规则文件中就没办法使用facts这个参数,因为规则文件中无法传入facts这个参数;

    在这里插入图片描述

    在这里插入图片描述

  2. 通过规则监听处理自定义规则的逻辑处理

    基于上一个问题,我想到了使用规则监听来进行处理,可以在规则监听类中的afterEvaluate方法中进行相关的逻辑编写,evaluationResult参数时布尔类型的,表示规则匹配是否通过,通过值为true,未通过值未false,也可以使用facts和rule,还是很不错的;

    监听触发规则添加学历集合

  3. 使用监听则会作用到所有执行的规则,如何准确处理

    基于第二个问题,发现使用了规则监听后,如果你设置的了多个规则进行匹配,那么每次规则执行都会走到afterEvaluate方法中,所以这里的代码逻辑就可能被重复执行多次,这里我处理方法是通过规则的名称来匹配,只有是指定名称的规则才会走逻辑处理。

    匹配规则执行逻辑

代码实现

场景简述

接下来使用的测试场景是,输入一个人的信息,信息中包含了这个人的学历等级,根据学历等级去查询他的学历证书情况(集合存储),查询完学历证书后,在检测学历证书与他的学历等级是否匹配,匹配规则为,学历证书数量与学历等级相同,并且最大学历证书的学历等级与人的学历等级一致,则匹配通过,最终人的信息中会添加学历匹配结果,匹配通过的为学历真实,未匹配通过的为存在学历造假嫌疑,并且会保存人的学历资质集合信息。

图解

在这里插入图片描述

java规则类实现

规则类

CheckIt:通过检测学历真实

package entity.rules.education;

import entity.pojo.Education;
import entity.pojo.Person;
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Rule;
import org.jeasy.rules.api.Facts;

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 莫须有
 * @Date 2021/12/31 15:45
 * @Description 检测学历真实性
 */
@Rule(name = "check", description = "检测学历真实性")
public class CheckIt {
    @Condition
    public boolean check(@Fact("person")Person person, Facts facts){
        List<Education> educationList = person.getEducationList();

        List<Education> collect = educationList.stream().sorted(Comparator.comparing(Education::getGrade).reversed()).collect(Collectors.toList());

        person.setEducationList(collect);
        facts.put("person", person);
        return person.getQualifications() == educationList.size() && collect.get(0).getGrade() == person.getQualifications();
    }

    @Action
    public void set(Facts facts){

        Person person = facts.get("person");
        person.setDec("学历真实!");

        facts.put("person", person);
    }
}

EducationAdd:添加学历资质集合

package entity.rules.education;

import entity.pojo.Person;
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Rule;
import org.jeasy.rules.api.Facts;
import utils.AddEducation;

import java.util.List;

/**
 * @author 莫须有
 * @Date 2021/12/31 15:22
 * @Description 根据学历添加学历集合
 */
@Rule(name = "EducationAdd", description = "添加学历规则匹配")
public class EducationAdd {

    @Condition
    public boolean educationAdd(@Fact("person") Person person){
        return person.getQualifications() >=0 &&  person.getQualifications() <= 10;
    }

    @Action
    public void add(Facts facts){
        Person person = facts.get("person");

        List list = AddEducation.getQualification(person);

        person.setEducationList(list);

        facts.put("person", person);
    }
}

QualificationNotMach:不符合要求的学历,即造假学历

package entity.rules.education;

import entity.pojo.Education;
import entity.pojo.Person;
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Rule;
import org.jeasy.rules.api.Facts;

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 莫须有
 * @Date 2021/12/31 17:51
 * @Description 学历造假规则匹配
 */
@Rule(name = "QualificationNotMach", description = "学历检测未通过匹配规则")
public class QualificationNotMach {

    @Condition
    public boolean qualificationNotMach(@Fact("person")Person person, Facts facts){
        List<Education> educationList = person.getEducationList();

        List<Education> collect = educationList.stream().sorted(Comparator.comparing(Education::getGrade).reversed()).collect(Collectors.toList());

        person.setEducationList(collect);
        facts.put("person", person);
        return !(person.getQualifications() == educationList.size() && collect.get(0).getGrade() == person.getQualifications());
    }

    @Action
    public void add(Facts facts){
        Person person = facts.get("person");
        person.setDec("存在学历造假的嫌疑!");

        facts.put("person", person);
    }
}

AddEducation :添加学历资质集合工具类

package utils;

import entity.pojo.Education;
import entity.pojo.Person;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 莫须有
 * @Date 2021/12/31 14:32
 * @Description 添加学历集合
 */
public class AddEducation {

    public static List getQualification(Person person){
        List qList = new ArrayList();
        switch (person.getQualifications()){
            case 0:
                qList.add(getGrade0());
                break;
            case 1:
                qList.add(getGrade1());
                break;
            case 2:
                qList.add(getGrade1());
                qList.add(getGrade3());
                break;
            case 3:
               ····
            default:
                break;

        }
        return qList;
    }

    private static Education getGrade0() {
        Education Education = new Education();
        Education.setGrade(0);
        Education.setQualificationsName("文盲");
        Education.setSchoolName("无教育经历");
        return Education;
    }

    ····
}

测试类:

package test;

import config.MyRulesListener;
import entity.pojo.Person;
import entity.rules.education.CheckIt;
import entity.rules.education.EducationAdd;
import entity.rules.education.QualificationNotMach;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.core.DefaultRulesEngine;

/**
 * @author 莫须有
 * @Date 2021/12/31 15:11
 * @Description 学历规则匹配测试
 */
public class EducationTest {

    public static void main(String[] args) {
        Person person = new Person();
        person.setName("莫须有");
        person.setAge(25);
        person.setQualifications(6);

        Facts facts = new Facts();
        facts.put("person", person);

        Rules rules = new Rules();
        rules.register(new EducationAdd());
        rules.register(new CheckIt());
        rules.register(new QualificationNotMach());

        DefaultRulesEngine engine = new DefaultRulesEngine();
        engine.fire(rules, facts);

        System.out.println("----------------------------");
        System.out.println(person);
    }
}

执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FkiYzHOc-1642052913427)(E:\suredata\markdown\img\规则类执行结果.png)]

以上时java规则类实现学历检测的代码实现。

自定义规则文件实现

在这里插入图片描述

这里加入了一个学历资质11,因为之前是只能匹配到10,由于是测试,所以在学历资质添加类里面也加入了对应11的资质添加代码,如果是真实场景就可以是从数据库查询,就不需要进行修改。

AddEducation :添加学历资质集合工具类(添加11)

package utils;

import entity.pojo.Education;
import entity.pojo.Person;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 莫须有
 * @Date 2021/12/31 14:32
 * @Description 添加学历集合
 */
public class AddEducation {

    public static List getQualification(Person person){
        List qList = new ArrayList();
        switch (person.getQualifications()){
            case 0:
                qList.add(getGrade0());
                break;
            case 1:
                qList.add(getGrade1());
                break;
            case 2:
                qList.add(getGrade1());
                qList.add(getGrade3());
                break;
            ····
            default:

        }
        return qList;
    }

    private static Education getGrade0() {
        Education Education = new Education();
        Education.setGrade(0);
        Education.setQualificationsName("文盲");
        Education.setSchoolName("无教育经历");
        return Education;
    }

    ····

}

规则文件(这里与json文件为例,yml文件类似)

EducationAddJsonFile.json

[
	{
      "name": "newEducationAdd",
      "description": "修改学历添加列表",
      "condition": "person.getQualifications() >= 0 && person.getQualifications()<=11",
      "priority": 3,
      "actions": [
        "System.out.println(\"新规则执行了\")"
      ]
	}
]

在规则文件中actions是规则执行的内容,由于上面问题2中的问题,所以文件中只是进行condition条件的编写,actions中的逻辑处理放到监听类的afterEvaluate方法中进行处理,但是actions内容不能未空,所以随便写一个打印语句。

监听类 MyRulesListener:

package config;

import entity.pojo.Person;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.RuleListener;
import utils.AddEducation;

import java.util.List;

/**
 * @author 莫须有
 * @Date 2021/12/30 18:20
 * @Description 规则监听器
 */
public class MyRulesListener implements RuleListener {
    @Override
    public boolean beforeEvaluate(Rule rule, Facts facts) {
        System.out.println("beforeEvaluate 触发");
        return true;
    }

    @Override
    public void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) {
        if (evaluationResult && rule.getName().equals(facts.get("ruleName"))){
            Person person = facts.get("person");

            List list = AddEducation.getQualification(person);

            person.setEducationList(list);

            facts.put("person", person);
        }
        System.out.println("afterEvaluate 触发");
    }

    @Override
    public void onEvaluationError(Rule rule, Facts facts, Exception exception) {
        System.out.println("onEvaluationError 触发");
    }

    @Override
    public void beforeExecute(Rule rule, Facts facts) {
        System.out.println("beforeExecute 触发");
    }

    @Override
    public void onSuccess(Rule rule, Facts facts) {
        System.out.println("onSuccess 触发");
    }

    @Override
    public void onFailure(Rule rule, Facts facts, Exception exception) {
        System.out.println("onFailure 触发");
    }
}

测试类 TestUploudFile:

package test;

import config.MyEngineListener;
import config.MyRulesListener;
import entity.pojo.Person;
import entity.rules.education.CheckIt;
import entity.rules.education.QualificationNotMach;
import lombok.extern.slf4j.Slf4j;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.mvel.MVELRuleFactory;
import org.jeasy.rules.support.reader.JsonRuleDefinitionReader;
import org.jeasy.rules.support.reader.RuleDefinitionReader;
import org.jeasy.rules.support.reader.YamlRuleDefinitionReader;

import java.io.FileReader;

/**
 * @author 莫须有
 * @Date 2022/1/10 9:46
 * @Description 测试文件上传
 */
@Slf4j
public class TestUploudFile {

    public static void main(String[] args) {
//        MVELRuleFactory factory = new MVELRuleFactory(new YamlRuleDefinitionReader());
        MVELRuleFactory factory = new MVELRuleFactory(new JsonRuleDefinitionReader());

        // 读取json文件
        String path = JsonTest.class.getResource("/rules/EducationAddJsonFile.json").getPath();
//        String path = JsonTest.class.getResource("/rules/EducationAddYmlFile.yml").getPath();

        try {
            FileReader jsonFile = new FileReader(path);
            Rule rule = factory.createRule(jsonFile);
            String ruleName = rule.getName();

            Rules rules = new Rules();
            rules.register(rule);
            rules.register(new CheckIt());
            rules.register(new QualificationNotMach());

            Person person = new Person();
            person.setName("莫须有");
            person.setAge(25);
            person.setQualifications(11);

            Facts facts = new Facts();
            facts.put("person", person);
            facts.put("ruleName", ruleName);

            DefaultRulesEngine engine = new DefaultRulesEngine();
            engine.registerRuleListener(new MyRulesListener());
            engine.fire(rules, facts);

            System.out.println(person);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8A9NSyRL-1642052913428)(E:\suredata\markdown\img\json规则文件执行结果.png)]

以上是自定义规则文件执行规则实现。

条件语句调用方法处理

以上内容完成后,在老大的引导下对自定义文件中condition内容进行探讨,是否可以调用方法来进行判断,测试后发现,可以调用方法进行判断,但是对调用方法的位置有要求,方法只能是facts对象的参数对象内部定义的方法,也就是说在上述例子中,只能使用person中定义的方法,后续测试中尝试了通过person中的方法在调用外部方法也是可以的,所以虽然condition中需要强依赖于facts中的内容,但是可以通过facts中的对象调用其他外部方法实现规则判断。

测试代码

json文件:

[{
  "name": "newEducationAdd",
  "description": "修改学历添加列表",
  "condition": "person.getResult(person.getQualifications())",
  "priority": 3,
  "actions": [
    "System.out.println(\"新规则执行了\")"
  ]
}]

person类

package entity.pojo;

import entity.rules.education.EducationAdd;
import lombok.Data;
import utils.AddEducation;

import java.util.List;

/**
 * @author 莫须有
 * @Date 2021/12/31 13:59
 * @Description
 */
@Data
public class Person {
    // 姓名
    private String name;

    // 年龄
    private int age;

    // 描述
    private String dec;

    // 学历等级
    private int qualifications;

    private List<Education> educationList;

    public boolean getResult(int i){
        return AddEducation.getResult(i);
//        return i >=0 && i <= 11;
    }
}

AddEducation类中添加getResult方法:


/**
 * @author 莫须有
 * @Date 2021/12/31 14:32
 * @Description 添加学历集合
 */
public class AddEducation {

    public static boolean getResult(int i){
        System.out.println("111111");
        return i >= 0 && i <= 11;
    }

}

启动测试类:

package test;

import config.MyEngineListener;
import config.MyRulesListener;
import entity.pojo.Person;
import entity.rules.education.CheckIt;
import entity.rules.education.QualificationNotMach;
import lombok.extern.slf4j.Slf4j;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.mvel.MVELRuleFactory;
import org.jeasy.rules.support.reader.JsonRuleDefinitionReader;
import org.jeasy.rules.support.reader.RuleDefinitionReader;
import org.jeasy.rules.support.reader.YamlRuleDefinitionReader;

import java.io.FileReader;

/**
 * @author 莫须有
 * @Date 2022/1/10 9:46
 * @Description 测试文件上传
 */
@Slf4j
public class TestUploudFile {

    public static void main(String[] args) {
//        MVELRuleFactory factory = new MVELRuleFactory(new YamlRuleDefinitionReader());
        MVELRuleFactory factory = new MVELRuleFactory(new JsonRuleDefinitionReader());

        // 读取json文件
        String path = JsonTest.class.getResource("/rules/EducationAddJsonFile.json").getPath();
//        String path = YmlTest.class.getResource("/rules/EducationAddYmlFile.yml").getPath();

        try {
            FileReader jsonFile = new FileReader(path);
            Rule rule = factory.createRule(jsonFile);
            String ruleName = rule.getName();

            Rules rules = new Rules();
            rules.register(rule);
            rules.register(new CheckIt());
            rules.register(new QualificationNotMach());

            Person person = new Person();
            person.setName("莫须有");
            person.setAge(25);
            person.setQualifications(11);

            Facts facts = new Facts();
            facts.put("person", person);
            facts.put("ruleName", ruleName);

            DefaultRulesEngine engine = new DefaultRulesEngine();
            engine.registerRuleListener(new MyRulesListener());
            engine.fire(rules, facts);

            System.out.println(person);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

总结

easyrules自定义规则通过json和yml文件来实现,可以适应变化的需求,有效避免重复修改代码逻辑,是一个很好的if-else替代品;并且它的condition判断可以通过调用方法来进行处理,这对业务的可扩展性也有很大的帮助,后续可以将规则存入数据库,存储内容可以以json字符串格式存储,只是在使用规则的时候需要将规则字符串转换为json文件,需要注意的是,json文件中的规则需要以数组形式存放,无论是一个还是多个。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
easyrules是一个开源的Java规则引擎,可以用于解耦业务逻辑和规则实现easyrules动态编译是指在运行时动态生成和编译规则,以达到灵活性和可扩展性的目的。 easyrules动态编译主要有两个方面的作用。首先,它为用户提供了一种动态添加和修改规则的方式。通过动态编译,用户可以在运行时根据实际需要添加或修改规则,无需重新启动系统或重新编译代码。这使得系统更加灵活,可以根据具体场景来定制规则,提高了系统的可扩展性和适应性。 其次,easyrules动态编译还提供了一种动态加载规则的机制。用户可以将规则定义存储在外部文件中,通过动态编译,easyrules可以在系统启动时加载这些规则,并根据实际需求进行编译和生效。这种方式可以将规则与系统业务逻辑分离,提高代码的可读性和可维护性。 实现easyrules动态编译的关键在于使用Java动态编译工具,如JavaCompiler,来在运行时生成和编译规则代码。通过这种方式,用户可以通过编写一些简单的规则定义代码,然后在运行时通过动态编译生成完整的规则代码,并加载到系统中运行。这样既避免了手动编写大量重复的规则代码,又能够保证规则的灵活性和可扩展性。 总之,easyrules动态编译是easyrules规则引擎的一个重要特性,它提供了一种灵活的方式来添加、修改和加载规则,提高了系统的可扩展性和适应性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值