溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java規則引擎easy-rules如何理解

發布時間:2022-01-04 12:58:52 來源:億速云 閱讀:211 作者:柒染 欄目:開發技術

Java規則引擎easy-rules如何理解,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

    最近在思考一個基于規則進行挑選的技術重構,想通過規則引擎進行實現,借著這個機會正好可以詳細了解一下規則引擎。下面將會詳細介紹規則引擎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,否則為false
        */
        boolean evaluate(Facts facts);
    
        /**
        * 此方法封裝了規則的操作。
        * @throws 如果在執行操作期間發生錯誤,則拋出異常
        */
        void execute(Facts facts) throws Exception;
    
        //Getters and setters 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注解用來標記執行操作的方法,規則可以有多個操作??梢允褂胦rder屬性以指定的順序執行操作。

    使用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);
    
    RulesEngine rulesEngine = new DefaultRulesEngine();
    rulesEngine.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參數中:

    @Rule
    class 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的參數將被注入所有已知的事實。
    注意:

    • 如果條件方法中缺少注入的事實,引擎將記錄一個警告,并認為條件被計算為false。

    • 如果動作方法中缺少注入的事實,則不會執行該動作,并且拋出org.jeasy.rules.core.NoSuchFactException異常。

    定義規則引擎

    Easy Rules提供了RulesEngine接口的兩種實現:

    • DefaultRulesEngine:根據規則的自然順序(默認為優先級)應用規則。

    • InferenceRulesEngine:在已知的事實上不斷地應用規則,直到沒有更多的規則可用。

    創建規則引擎

    可以使用構造方法創建規則引擎。

    RulesEngine rulesEngine = new DefaultRulesEngine();
    // or
    RulesEngine rulesEngine = new InferenceRulesEngine();

    可以按如下方式觸發已注冊的規則。

    rulesEngine.fire(rules, facts);
    規則引擎參數

    Easy Rules引擎可以配置以下參數:

    參數類型默認值
    rulePriorityThresholdintMaxInt
    skipOnFirstAppliedRulebooleanfalse
    rulePriorityThresholdintfalse
    skipOnFirstFailedRulebooleanfalse
    skipOnFirstNonTriggeredRulebooleanfalse
    • skipOnFirstAppliedRule:當一個規則成功應用時,跳過余下的規則。

    • skipOnFirstFailedRule:當一個規則失敗時,跳過余下的規則。

    • skipOnFirstNonTriggeredRule:當一個規則未觸發時,跳過余下的規則。

    • rulePriorityThreshold:當優先級超過指定的閾值時,跳過余下的規則。

    可以使用RulesEngineParameters API指定這些參數:

    RulesEngineParameters parameters = new RulesEngineParameters()
        .rulePriorityThreshold(10)
        .skipOnFirstAppliedRule(true)
        .skipOnFirstFailedRule(true)
        .skipOnFirstNonTriggeredRule(true);
    
    RulesEngine rulesEngine = new DefaultRulesEngine(parameters);

    如果你想從你的引擎中獲取參數,你可以使用以下代碼段:

    RulesEngineParameters parameters = myEngine.getParameters();

    這允許在創建引擎參數后重新設置引擎參數。

    定義規則監聽器

    可以通過RuleListener API來監聽規則執行事件:

    public interface RuleListener {
    
        /**
         * 在評估規則之前觸發。
         *
         * @param rule 正在被評估的規則
         * @param facts 評估規則之前的已知事實
         * @return 如果規則應該評估,則返回true,否則返回false
         */
        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) { }
    
    }

    可以實現這個接口來提供自定義行為,以便在每個規則之前/之后執行。要注冊監聽器,請使用以下代碼段:

    DefaultRulesEngine rulesEngine = new DefaultRulesEngine();
    rulesEngine.registerRuleListener(myRuleListener);

    可以注冊任意數量的偵聽器,它們將按照注冊順序執行。
    注意:當使用組合規則時,監聽器是圍繞組合規則調用的。

    定義規則引擎監聽器

    可以通過RulesEngineListener API來監聽規則引擎的執行事件:

    public interface RulesEngineListener {
    
        /**
         * 在執行規則集之前觸發
         *
         * @param rules 要觸發的規則集
         * @param facts 觸發規則前的事實
         */
        default void beforeEvaluate(Rules rules, Facts facts) { }
    
        /**
         * 在執行規則集之后觸發
         *
         * @param rules 要觸發的規則集
         * @param facts 觸發規則前的事實
         */
        default void afterExecute(Rules rules, Facts facts) { }
    }

    RulesEngineListener允許我們在觸發整個規則集之前/之后提供自定義行為??梢允褂萌缦路绞阶员O聽器。

    DefaultRulesEngine rulesEngine = new DefaultRulesEngine();
    rulesEngine.registerRulesEngineListener(myRulesEngineListener);

    可以注冊任意數量的監聽器,它們將按照注冊順序執行。

    表達式語言(EL)支持

    Easy Rules支持用MVEL、SpEL和JEXL定義規則。

    EL提供者注意事項

    EL提供者在行為上有一些區別。例如,當一個事實在條件中缺失時,MVEL拋出一個異常,而SpEL將忽略它并返回false。因此,在選擇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.setAdult(true);");
    通過規則描述文件定義規則

    可以使用規則描述文件定義規則,使用MVELRuleFactory/SpELRuleFactory/JexlRuleFactory來從描述符文件創建規則。下面是一個在alcohol-rule.yml中以YAML格式定義的MVEL規則示例:

    name: "alcohol rule"
    description: "children are not allowed to buy alcohol"
    priority: 2
    condition: "person.isAdult() == false"
    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 rule
    description: when age is greater than 18, then mark as adult
    priority: 1
    condition: "person.age > 18"
    actions:
      - "person.setAdult(true);"
    ---
    name: weather rule
    description: when it rains, then take an umbrella
    priority: 2
    condition: "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描述符加載規則。具體參考文檔,這里不做展開。

    規則定義中的錯誤處理

    關于條件中不正確表達式的引擎行為
    對于條件求值過程中可能發生的任何運行時異常(丟失事實、表達式中輸入錯誤等),引擎將記錄一個警告,并認為條件求值為false??梢允褂肦uleListener#onEvaluationError來監聽評估錯誤。

    關于操作中不正確表達式的引擎行為
    對于任何在執行操作時可能發生的運行時異常(丟失事實、表達式中輸入錯誤等),該操作將不會執行,引擎將記錄一個錯誤??梢允褂肦uleListener#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");
          else if ((i % 5) == 0) System.out.print("fizz");
          else if ((i % 7) == 0) System.out.print("buzz");
          else System.out.print(i);
          System.out.println();
        }
        System.out.println();
      }
    }

    我們將為每個需求編寫一條規則:

    @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;
        }
    }
    
    @Rule
    public 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;
        }
    }
    
    @Rule
    public 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創建?;愡x擇為UnitRuleGroup,要么滿足并應用這兩個規則,要么什么都不應用。

    • NonFizzBuzzRule是既不是5的倍數也不是7的倍數時的規則。

    請注意,我們已經設置了優先級,因此規則的觸發順序與Java示例中的示例相同。
    然后,我們必須將這些規則注冊到一個規則集中,并使用一個規則引擎來觸發它們:

    public class FizzBuzzWithEasyRules {
        public static void main(String[] args) {
            // 創建規則引擎
            RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true);
            RulesEngine fizzBuzzEngine = new DefaultRulesEngine(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 Facts();
            for (int i = 1; i <= 100; i++) {
                facts.put("number", i);
                fizzBuzzEngine.fire(rules, facts);
                System.out.println();
            }
        }
    }

    注意,我們已經設置了skipOnFirstAppliedRule參數,以便在成功應用規則時跳過后續的規則。

    看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女