首页 > 作文

Java规则引擎easy

更新时间:2023-04-04 09:48:57 阅读: 评论:0

最近在思考一个基于规则进行挑选的技术重构,想通过规则引擎进行实现,借着这个机会正好可以详细了解一下规则引擎。本篇文章将会详细介绍规则引擎easy-rules的使用。项目地址:https://github.com/j-easy/easy-rules

简介

easy rules是一个简单但功能强大的java规则引擎,提供以下特性:

轻量级框架和易于学习的api基于pojo的开发支持从原始规则创建组合规则支持通过表达式(如mvel,spel和jexl)定义规则

开始使用

引入依赖

<dependency>    <groupid>org.jeasy</groupid>    <artifactid>easy-rules-core</artifactid>    <version>4.1.0</version></dependency>

上面只引入了core模块依赖,如需要其它模块内容,再引入对应依赖即可。

定义规则

介绍

大多数业务规则可以用以下定义表示:

name:规则命名空间中的唯一规则名称description:规则的简要描述priority:规则的优先级facts:触发规则时的一组已知事实conditions:在给定一些事实的情况下,为了应用该规则,需要满足的一组条件actions:满足条件时要执行的一组操作(可能会添加/删除/修改事实)

easy rules为定义业务规则的每个关键点提供了抽象。easy rules中的规则由rule接口表示:

public interface rule extends comparable<rule> {    /**    * 此方法封装了规则的条件。    * @return 如果根据提供的事实可以应用规则,则为true,否则为fal    */    boolean evaluate(facts facts);    /**    * 此方法封装了规则的操作。    * @throws 如果在执行操作期间发生错误,则抛出异常    */    void execute(facts facts) throws exception;    //getters and tters for rule name, description and priority omitted.}

evaluate()方法封装了必须为true才能触发规则的条件。execute()方法封装了在满足规则条件时应该执行的操作。条件和操作由condition和action接口表示。
规则可以用两种不同的方式定义:

通过在pojo上添加注解来声明通过rulebuilder api编程

这些是定义规则的最常用方法,但是如果需要,您也可以实现rule接口或扩展basicrule类。

使用注解定义规则

easy rules提供了@rule注解,可以将pojo转换为规则。

@rule(name = "my rule", description = "my rule description", priority = 1)public class myrule {  @condition  public boolean when(@fact("fact") fact) {    // 规则条件    return true;  }  @action(order = 1)  public void then(facts facts) throws exception {    // 规则为true时的操作1  }  @action(order = 2)  public void finally() throws exception {    // 规则为true时的操作2  }}

@condition注解用来标记评估规则条件的方法,这个方法必须是public,可以有一个或多个带@fact注解的参数,并返回一个boolean类型。只有一个方法可以用@condition注解标记。
@action注解用来标记执行操作的方法,规则可以有多个操作。可以使用order属性以指定的顺序执行操作。

使用rulebuilder定义规则

rulebuilder允许你用流式api定义规则。

rule rule = new rulebuilder()                .name("myrule")                .description("myruledescription")                .priority(3)                .when(condition)                .then(action1)                .then(action2)                .build();

在本例中,condition是condition接口的实例,action1和action2是action接口的实例。

组合规则

easy rules允许从原始规则创建复杂的规则。一个compositerule由一组规则组成。组合规则是一个抽象概念,因为组合规则可以以不同的方式触发。easy rules提供了3种compositerule的实现。

unitrulegroup:单元规则组是作为一个单元使用的组合规则,要么应用所有规则,要么不应用任何规则。activationrulegroup:激活规则组触发第一个适用规则并忽略组中的其他规则。规则首先按照其在组中的自然顺序(默认情况下优先级)进行排序。conditionalrulegroup:条件规则组将具有最高优先级的规则作为条件,如果具有最高优先级的规则的计算结果为true,那么将触发其余的规则。

组合规则可以从原始规则创建并像常规规则一样注册。

// 从两个原始规则创建组合规则unitrulegroup myunitrulegroup =  new unitrulegroup("myunitrulegroup", "unit of myrule1 and myrule2");myunitrulegroup.addrule(myrule1);myunitrulegroup.addrule(myrule2);// 像常规规则一样注册组合规则rules rules = new rules();rules.register(myunitrulegroup);rulengine rulengine = new defaultrulengine();rulengine.fire(rules, somefacts);

规则优先级

easy rules中的每个规则都有一个优先级。这表示触发注册规则的默认顺序。默认情况下,值越低优先级越高。要覆盖此行为,您应该重写compareto()方法以提供自定义优先级策略。

如果是继承basicrule,可以在构造方法中指定优先级,或者重写getpriority()方法。如果是使用pojo定义规则,可以通过@rule注解的priority属性指定优先级,或者使用@priority注解标记一个方法。这个方法必须是public,无参却返回类型为integer。如果使用rulebuilder定义规则,可以使用rulebuilder#priority()方法指定优先级。

rules api

easy rules中的一组规则由rules api表示。它的使用方法如下:

rules rules = new rules();rules.register(myrule1);rules.register(myrule2);

rules表示已注册规则的命名空间,因此,在同一命名空间下,每一个已经注册的规则必须有唯一的名称。

rules是通过rule#compareto()方法进行比较的,因此,rule的实现应该正确的实现compareto()方法来确保单一空间下拥有唯一的规则名称。

定义事实

easy rules中的一个事实是由fact表示的:

public class fact<t> { 今年五一节放假安排  private final string name;   private final t value;}

一个事实有一个名称和一个值,两者都不能为null。另一方面,facts api 表示一组事实并充当事实的命名空间。这意味着,在一个facts实例中,事实必须有唯一的名称。
下面是一个如何定义事实的例子:

fact<string> fact = new fact("foo", "bar");facts facts = new facts();facts.add(fact);

你也可以使用一个更短的版本,用put方法创建命名的事实,如下所示:

facts facts = new facts();facts.put("foo", "bar");

可以使用@fact注解将事实注入到规则的条件和操作方法中。在以下规则中,rain事实被注入到itrains方法的rain参数中:

@ruleclass weatherrule {  @condition  public boolean itrains(@fact("rain") boolean rain) {    return rain;  }  @action  public void takeanumbrella(facts facts) {    system.out.println("it rains, take an umbrella!");    // can add/remove/modify facts  }}

类型为facts的参数将被注入所有已知的事实。
注意:

如果条件方法中缺少注入的事实,引擎将记录一个警告,并认为条件被计算为fal。如果动作方法中缺少注入的事实,则不会执行该动作,并且抛出org.jeasy.rules.core.nosuchfactexception异常。

定义规则引擎

easy rules提供了rulengine接口的两种实现:

defaultrulengine:根据规则的自然顺序(默认为优先级)应用规则。inferencerulengine:日本冲绳岛在已知的事实上不断地应用规则,直到没有更多的规则可用。

创建规则引擎

给家长的一封信作文可以使用构造方法创建规则引擎。

rulengine rulengine = new defaultrulengine();// orrulengine rulengine = new inferencerulengine();

可以按如下方式触发已注册的规则。

rulengine.fire(rules, facts);

规则引擎参数

easy rules引擎可以配置以下参数:

参数类型默认值ruleprioritythresholdintmaxintskiponfirstappliedrulebooleanfalruleprioritythresholdintfalskiponfirstfailedrulebooleanfalskiponfirstnontriggeredrulebooleanfalskiponfirstappliedrule:当一个规则成功应用时,跳过余下的规则。skiponfirstfailedrule:当一个规则失败时,跳过余下的规则。skiponfirstnontriggeredrule:当一个规则未触发时,跳过余下的规则。ruleprioritythreshold:当优先级超过指定的阈值时,跳过余下的规则。

可以使用rulengineparameters api指定这些参数:

rulengineparameters parameters = new rulengineparameters()  .ruleprioritythreshold(10)  .skiponfirstappliedrule(true)  .skiponfirstfailedrule(true)  .skiponfirstnontriggeredrule(true);rulengine rulengine = new defaultrulengine(parameters);

如果你想从你的引擎中获取参数,你可以使用以下代码段:

rulengineparameters parameters = myengine.getparameters();

这允许在创建引擎参数后重新设置引擎参数。

定义规则监听器

可以通过rulelistener api来监听规则执行事件:

public interface rulelistener {  /**  * 在评估规则之前触发。  *  * @param rule 正在被评估的规则  * @param facts 评估规则之前的已知事实  * @return 如果规则应该评估,则返回true,否则返回fal  */  default boolean beforeevaluate(rule rule, facts facts) {    return true;  }  /**  * 在评估规则之后触发  *  * @param rule 评估之后的规则  * @param facts 评估规则之后的已知事实  * @param evaluationresult 评估结果  */  default void afterevaluate(rule rule, facts facts, boolean evaluationresult) { }  /**  * 运行时异常导致条件评估错误时触发  *  * @param rule 评估之后的规则  * @param facts 评估时的已知事实  * @param exception 条件评估时发生的异常  */  default void onevaluationerror(rule rule, facts facts, exception exception) { }  /**  * 在规则操作执行之前触发。  *  * @param rule 当前的规则  * @param facts 执行规则操作时的已知事实  */  default void beforeexecute(rule rule, facts facts) { }  /**  * 在规则操作成功执行之后触发  *  * @param rule t当前的规则  * @param facts 执行规则操作时的已知事实  */  default void onsuccess(rule rule, facts facts) { }  /**  * 在规则操作执行失败时触发  *  * @param rule 当前的规则  * @param facts 执行规则操作时的已知事实  * @param exception 执行规则操作时发生的异常  */  default void onfailure(rule rule, facts facts, exception exception) { }}

可以实现这个接口来提供自定义行为,以便在每个规则之前/之后执行。要注册监听器,请使用以下代码段:

defaultrulengine rulengine = new defaultrulengine();rulengine.registerrulelistener(myrulelistener);

可以注册任意数量的侦听器,它们将按照注册顺序执行。
注意:当使用组合规则时,监听器是围绕组合规则调用的。

定义规则引擎监听器

可以通过rulenginelistener api来监听规则引擎的执行事件:

public interface rulenginelistener {  /**  * 在执行规则集之前触发  *  * @param rules 要触发的规则集  * @param facts 触发规则前的事实  */  default void beforeevaluate(rules rules, facts facts) { }  /**  * 在执行规则集之后触发  *  * @param rules 要触发的规则集  * @param facts 触发规则前的事实  */  default void afterexecute(rules rules, facts facts) { }}

rulenginelistener允许我们在触发整个规则集之前/之后提供自定义行为。可以使用如下方式注册监听器。

defaultrulengine rulengine = new defaultrulengine();rulengine.registerrulenginelistener(myrulenginelistener);

可以注册任意数量的监听器,它们将按照注册顺序执行。

表达式语言(el)支持

easy rules支持用mvel、spel和jexl定义规则。

el提供者注意事项

el提供者在行为上有一些区别。例如,当一个事实在条件中缺失时,mvel抛出一个异常,而spel将忽略它并返回fal。因此,在选择easy rules使用哪个el之前,你应该了解这些差异。

通过编程的方式定义规则

条件、动作和规则分别由mvelcondition/spelcondition/jexlcondition、mvelaction/spelaction/jexlaction和mvelrule/spelrule/jexlrule类表示。下面是一个使用mvel定义规则的例子:

rule agerule = new mvelrule()        .name("age rule")        .description("check if person's age is > 18 and marks the person as adult")        .priority(1)        .when("person.age > 18")        .then("person.tadult(true);");

通过规则描述文件定义规则

可以使用规则描述文件定义规则,使用mvelrulefactory/spelrulefactory/jexlrulefactory来从描述符文件创建规则。下面是一个在alcohol-rule.yml中以yaml格式定义的mvel规则示例:

name: "alcohol rule"description: "children are not allowed to buy alcohol"priority: 2condition: "person.isadult() == fal"actions:  - "system.out.println("shop: sorry, you are not allowed to buy alcohol");"
mvelrulefactory rulefactory = new mvelrulefactory(new yamlruledefinitionreader());mvelrule alcoholrule = rulefactory.createrule(new filereader("alcohol-rule.yml"));

还可以使用一个文件创建多个规则。

---name: adult ruledescription: when age is greater than 18, then mark as adultpriority: 1condition: "person.age > 18"actions:  - "person.tadult(true);"---name: weather ruledescription: when it rains, then take an umbrellapriority: 2condition: "rain == true"actions:  - "system.out.println("it rains, take an umbrella!");"

可以使用如下方式将这些规则加载到rules对象中。

mvelrulefactory rulefactory = new mvelrulefactory(new yamlruledefinitionreader());rules rules = rulefactory.createrules(new filereader("rules.yml"));

easy rules还支持从json描述符加载规则。具体参考文档,这里不做展开。

规则定义中的错误处理

关于条件中不正确表达式的引擎行为
对于条件求值过程中可能发生的任何运行时异常(丢失事实、表达式中输入错误等),引擎将记录一个警告,并认为条件求值为fal。可以使用rulelistener#onevaluationerror来监听评估错误。

关于操作中不正确表达式的引擎行为
对于任何在执行操作时可能发生的运行时异常(丢失事实、表达式中输入错误等),该操作将不会执行,引擎将记录一个错误。可以使用rulelistener#onfailure来监听操作执行异常。当一个规则失败时,引擎将移动到下一个规则,除非设置了skiponfirstfailedrule参数。

实际栗子

本栗子使用easy rules实现fizzbuzz应用程序。fizzbuzz是一个简单的应用程序,需要从1数到100,并且:

如果数字是5的倍数,则打印“fizz”如果数字是7的倍数,请打印“buzz”如果数字是5和7的倍数,请打印“fizzbuzz”否则打印数字本身
public class fizzbuzz {  public static void main(string[] args) {    for(int i = 1; i <= 100; i++) {      if (((i % 5) == 0) && ((i % 7) == 0))        system.out.print("fizzbuzz");      el if ((i % 5) == 0) system.out.print("fizz");      el if ((i % 7) == 0) system.out.print("buzz");      el system.out.print(i);      system.out.println();    }    system.out.println();  }}

我们将为每个需求编写一条规则:

@rulepublic class fizzrule {  @condition  public boolean isfizz(@fact("number") integer number) {    return number % 5 == 0;  }  @action  public void printfizz() {    s主人下马客在船下一句ystem.out.print("fizz");  }  @priority  public int getpriority() {    return 1;  }}@rulepublic class buzzrule {  @condition  public boolean isbuzz(@fact("number") integer number) {    return number % 7 == 0;  }  @action  public void printbuzz() {    system.out.print("buzz");  }  @priority  public int getpriority() {    return 2;  }}public class fizzbuzzrule extends unitrulegroup {  public fizzbuzzrule(object... rules) {    for (object rule : rules) {      addrule(rule);    }  }  @override  public int getpriority() {    return 0;  }}@rulepublic class nonfizzbuzzrule {  @condition  public boolean isnotfizznorbuzz(@fact("number") integer number) {    return number % 5 != 0 || number % 7 != 0;  }  @action  public void printinput(@fact("number") integer number) {    system.out.print(number);  }  @priority  public int getpriority() {    return 3;  }}

以下是对这些规则的一些解释:

fizzrule和buzzrule很简单,它们会检查输入是5的倍数还是7的倍数,然后打印结果。fizzbuzzrule是一个组合规则。通过fizzrule和buzzrule创建。基类选择为unitrulegroup,要么满足并应用这两个规则,要么什么都不应用。nonfizzbuzzrule是既不是5的倍数也不是7的倍数时的规则。

请注意,我们已经设置了优先级,因此规则的触发顺序与java示例中的示例相同。
然后,我们必须将这些规则注册到一个规则集中,并使用一个规则引擎来触发它们:

public class fizzbuzzwitheasyrules {  public static void main(string[] args) {    // 创建规则引擎    rulengineparameters parameters = new rulengineparameters().skiponfirstappliedrule(true);    rulengine fizzbuzzengine = new defaultrulengine(parameters);    // 创建规则    rules rules = new rules();    rules.register(new fizzrule());    rules.register(new buzzrule());    rules.register(new fizzbuzzrule(new fizzrule(), new buzzrule()));    rules.register(new nonfizzbuzzrule());    // 触发规则    facts facts = new f喜庆的日子acts();    for (int i = 1; i <= 100; i++) {      facts.put("number", i);      fizzbuzzengine.fire(rules, facts);      system.out.println();    }  }}

注意,我们已经设置了skiponfirstappliedrule参数,以便在成功应用规则时跳过后续的规则。

到此这篇关于java规则引擎easy-rules详细介绍的文章就介绍到这了,更多相关java规则引擎easy-rules详细介绍内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

本文发布于:2023-04-04 09:48:54,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/c757863462335e4c4cc4f1ff785265cf.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:Java规则引擎easy.doc

本文 PDF 下载地址:Java规则引擎easy.pdf

标签:规则   事实   引擎   定义
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图