Java規(guī)則引擎easy-rules詳細(xì)介紹
最近在思考一個(gè)基于規(guī)則進(jìn)行挑選的技術(shù)重構(gòu),想通過規(guī)則引擎進(jìn)行實(shí)現(xiàn),借著這個(gè)機(jī)會(huì)正好可以詳細(xì)了解一下規(guī)則引擎。本篇文章將會(huì)詳細(xì)介紹規(guī)則引擎easy-rules的使用。項(xiàng)目地址:https://github.com/j-easy/easy-rules
簡(jiǎn)介
Easy Rules是一個(gè)簡(jiǎn)單但功能強(qiáng)大的Java規(guī)則引擎,提供以下特性:
- 輕量級(jí)框架和易于學(xué)習(xí)的API
- 基于POJO的開發(fā)
- 支持從原始規(guī)則創(chuàng)建組合規(guī)則
- 支持通過表達(dá)式(如MVEL,SPEL和JEXL)定義規(guī)則
開始使用
引入依賴
<dependency> <groupId>org.jeasy</groupId> <artifactId>easy-rules-core</artifactId> <version>4.1.0</version> </dependency>
上面只引入了core模塊依賴,如需要其它模塊內(nèi)容,再引入對(duì)應(yīng)依賴即可。
定義規(guī)則
介紹
大多數(shù)業(yè)務(wù)規(guī)則可以用以下定義表示:
- name:規(guī)則命名空間中的唯一規(guī)則名稱
- description:規(guī)則的簡(jiǎn)要描述
- priority:規(guī)則的優(yōu)先級(jí)
- facts:觸發(fā)規(guī)則時(shí)的一組已知事實(shí)
- conditions:在給定一些事實(shí)的情況下,為了應(yīng)用該規(guī)則,需要滿足的一組條件
- actions:滿足條件時(shí)要執(zhí)行的一組操作(可能會(huì)添加/刪除/修改事實(shí))
Easy Rules為定義業(yè)務(wù)規(guī)則的每個(gè)關(guān)鍵點(diǎn)提供了抽象。Easy Rules中的規(guī)則由Rule接口表示:
public interface Rule extends Comparable<Rule> { /** * 此方法封裝了規(guī)則的條件。 * @return 如果根據(jù)提供的事實(shí)可以應(yīng)用規(guī)則,則為true,否則為false */ boolean evaluate(Facts facts); /** * 此方法封裝了規(guī)則的操作。 * @throws 如果在執(zhí)行操作期間發(fā)生錯(cuò)誤,則拋出異常 */ void execute(Facts facts) throws Exception; //Getters and setters for rule name, description and priority omitted. }
evaluate()方法封裝了必須為true才能觸發(fā)規(guī)則的條件。execute()方法封裝了在滿足規(guī)則條件時(shí)應(yīng)該執(zhí)行的操作。條件和操作由Condition和Action接口表示。
規(guī)則可以用兩種不同的方式定義:
- 通過在POJO上添加注解來聲明
- 通過RuleBuilder API編程
這些是定義規(guī)則的最常用方法,但是如果需要,您也可以實(shí)現(xiàn)Rule接口或擴(kuò)展BasicRule類。
使用注解定義規(guī)則
Easy Rules提供了@Rule注解,可以將POJO轉(zhuǎn)換為規(guī)則。
@Rule(name = "my rule", description = "my rule description", priority = 1) public class MyRule { ? ? @Condition ? ? public boolean when(@Fact("fact") fact) { ? ? ? ? // 規(guī)則條件 ? ? ? ? return true; ? ? } ? ? @Action(order = 1) ? ? public void then(Facts facts) throws Exception { ? ? ? ? // 規(guī)則為true時(shí)的操作1 ? ? } ? ? @Action(order = 2) ? ? public void finally() throws Exception { ? ? ? ? // 規(guī)則為true時(shí)的操作2 ? ? } }
@Condition注解用來標(biāo)記評(píng)估規(guī)則條件的方法,這個(gè)方法必須是public,可以有一個(gè)或多個(gè)帶@Fact注解的參數(shù),并返回一個(gè)boolean類型。只有一個(gè)方法可以用@Condition注解標(biāo)記。
@Action注解用來標(biāo)記執(zhí)行操作的方法,規(guī)則可以有多個(gè)操作??梢允褂胦rder屬性以指定的順序執(zhí)行操作。
使用RuleBuilder定義規(guī)則
RuleBuilder允許你用流式API定義規(guī)則。
Rule rule = new RuleBuilder() .name("myRule") .description("myRuleDescription") .priority(3) .when(condition) .then(action1) .then(action2) .build();
在本例中,condition是Condition接口的實(shí)例,action1和action2是Action接口的實(shí)例。
組合規(guī)則
Easy Rules允許從原始規(guī)則創(chuàng)建復(fù)雜的規(guī)則。一個(gè)CompositeRule由一組規(guī)則組成。組合規(guī)則是一個(gè)抽象概念,因?yàn)榻M合規(guī)則可以以不同的方式觸發(fā)。Easy Rules提供了3種CompositeRule的實(shí)現(xiàn)。
- UnitRuleGroup:?jiǎn)卧?guī)則組是作為一個(gè)單元使用的組合規(guī)則,要么應(yīng)用所有規(guī)則,要么不應(yīng)用任何規(guī)則。
- ActivationRuleGroup:激活規(guī)則組觸發(fā)第一個(gè)適用規(guī)則并忽略組中的其他規(guī)則。規(guī)則首先按照其在組中的自然順序(默認(rèn)情況下優(yōu)先級(jí))進(jìn)行排序。
- ConditionalRuleGroup:條件規(guī)則組將具有最高優(yōu)先級(jí)的規(guī)則作為條件,如果具有最高優(yōu)先級(jí)的規(guī)則的計(jì)算結(jié)果為true,那么將觸發(fā)其余的規(guī)則。
組合規(guī)則可以從原始規(guī)則創(chuàng)建并像常規(guī)規(guī)則一樣注冊(cè)。
// 從兩個(gè)原始規(guī)則創(chuàng)建組合規(guī)則 UnitRuleGroup myUnitRuleGroup = ? ? new UnitRuleGroup("myUnitRuleGroup", "unit of myRule1 and myRule2"); myUnitRuleGroup.addRule(myRule1); myUnitRuleGroup.addRule(myRule2); // 像常規(guī)規(guī)則一樣注冊(cè)組合規(guī)則 Rules rules = new Rules(); rules.register(myUnitRuleGroup); RulesEngine rulesEngine = new DefaultRulesEngine(); rulesEngine.fire(rules, someFacts);
規(guī)則優(yōu)先級(jí)
Easy Rules中的每個(gè)規(guī)則都有一個(gè)優(yōu)先級(jí)。這表示觸發(fā)注冊(cè)規(guī)則的默認(rèn)順序。默認(rèn)情況下,值越低優(yōu)先級(jí)越高。要覆蓋此行為,您應(yīng)該重寫compareTo()方法以提供自定義優(yōu)先級(jí)策略。
- 如果是繼承BasicRule,可以在構(gòu)造方法中指定優(yōu)先級(jí),或者重寫getPriority()方法。
- 如果是使用POJO定義規(guī)則,可以通過@Rule注解的priority屬性指定優(yōu)先級(jí),或者使用@Priority注解標(biāo)記一個(gè)方法。這個(gè)方法必須是public,無參卻返回類型為Integer。
- 如果使用RuleBuilder定義規(guī)則,可以使用RuleBuilder#priority()方法指定優(yōu)先級(jí)。
Rules API
Easy rules中的一組規(guī)則由rules API表示。它的使用方法如下:
Rules rules = new Rules(); rules.register(myRule1); rules.register(myRule2);
Rules表示已注冊(cè)規(guī)則的命名空間,因此,在同一命名空間下,每一個(gè)已經(jīng)注冊(cè)的規(guī)則必須有唯一的名稱。
Rules是通過Rule#compareTo()方法進(jìn)行比較的,因此,Rule的實(shí)現(xiàn)應(yīng)該正確的實(shí)現(xiàn)compareTo()方法來確保單一空間下?lián)碛形ㄒ坏囊?guī)則名稱。
定義事實(shí)
Easy Rules中的一個(gè)事實(shí)是由Fact表示的:
public class Fact<T> { private final String name; private final T value; }
一個(gè)事實(shí)有一個(gè)名稱和一個(gè)值,兩者都不能為null。另一方面,F(xiàn)acts API 表示一組事實(shí)并充當(dāng)事實(shí)的命名空間。這意味著,在一個(gè)Facts實(shí)例中,事實(shí)必須有唯一的名稱。
下面是一個(gè)如何定義事實(shí)的例子:
Fact<String> fact = new Fact("foo", "bar"); Facts facts = new Facts(); facts.add(fact);
你也可以使用一個(gè)更短的版本,用put方法創(chuàng)建命名的事實(shí),如下所示:
Facts facts = new Facts(); facts.put("foo", "bar");
可以使用@Fact注解將事實(shí)注入到規(guī)則的條件和操作方法中。在以下規(guī)則中,rain事實(shí)被注入到itRains方法的rain參數(shù)中:
@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的參數(shù)將被注入所有已知的事實(shí)。
注意:
- 如果條件方法中缺少注入的事實(shí),引擎將記錄一個(gè)警告,并認(rèn)為條件被計(jì)算為false。
- 如果動(dòng)作方法中缺少注入的事實(shí),則不會(huì)執(zhí)行該動(dòng)作,并且拋出org.jeasy.rules.core.NoSuchFactException異常。
定義規(guī)則引擎
Easy Rules提供了RulesEngine接口的兩種實(shí)現(xiàn):
- DefaultRulesEngine:根據(jù)規(guī)則的自然順序(默認(rèn)為優(yōu)先級(jí))應(yīng)用規(guī)則。
- InferenceRulesEngine:在已知的事實(shí)上不斷地應(yīng)用規(guī)則,直到?jīng)]有更多的規(guī)則可用。
創(chuàng)建規(guī)則引擎
可以使用構(gòu)造方法創(chuàng)建規(guī)則引擎。
RulesEngine rulesEngine = new DefaultRulesEngine(); // or RulesEngine rulesEngine = new InferenceRulesEngine();
可以按如下方式觸發(fā)已注冊(cè)的規(guī)則。
rulesEngine.fire(rules, facts);
規(guī)則引擎參數(shù)
Easy Rules引擎可以配置以下參數(shù):
參數(shù) | 類型 | 默認(rèn)值 |
---|---|---|
rulePriorityThreshold | int | MaxInt |
skipOnFirstAppliedRule | boolean | false |
rulePriorityThreshold | int | false |
skipOnFirstFailedRule | boolean | false |
skipOnFirstNonTriggeredRule | boolean | false |
skipOnFirstAppliedRule
:當(dāng)一個(gè)規(guī)則成功應(yīng)用時(shí),跳過余下的規(guī)則。skipOnFirstFailedRule
:當(dāng)一個(gè)規(guī)則失敗時(shí),跳過余下的規(guī)則。skipOnFirstNonTriggeredRule
:當(dāng)一個(gè)規(guī)則未觸發(fā)時(shí),跳過余下的規(guī)則。rulePriorityThreshold
:當(dāng)優(yōu)先級(jí)超過指定的閾值時(shí),跳過余下的規(guī)則。
可以使用RulesEngineParameters API指定這些參數(shù):
RulesEngineParameters parameters = new RulesEngineParameters() ? ? .rulePriorityThreshold(10) ? ? .skipOnFirstAppliedRule(true) ? ? .skipOnFirstFailedRule(true) ? ? .skipOnFirstNonTriggeredRule(true); RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
如果你想從你的引擎中獲取參數(shù),你可以使用以下代碼段:
RulesEngineParameters parameters = myEngine.getParameters();
這允許在創(chuàng)建引擎參數(shù)后重新設(shè)置引擎參數(shù)。
定義規(guī)則監(jiān)聽器
可以通過RuleListener API來監(jiān)聽規(guī)則執(zhí)行事件:
public interface RuleListener { ? ? /** ? ? ?* 在評(píng)估規(guī)則之前觸發(fā)。 ? ? ?* ? ? ?* @param rule 正在被評(píng)估的規(guī)則 ? ? ?* @param facts 評(píng)估規(guī)則之前的已知事實(shí) ? ? ?* @return 如果規(guī)則應(yīng)該評(píng)估,則返回true,否則返回false ? ? ?*/ ? ? default boolean beforeEvaluate(Rule rule, Facts facts) { ? ? ? ? return true; ? ? } ? ? /** ? ? ?* 在評(píng)估規(guī)則之后觸發(fā) ? ? ?* ? ? ?* @param rule 評(píng)估之后的規(guī)則 ? ? ?* @param facts 評(píng)估規(guī)則之后的已知事實(shí) ? ? ?* @param evaluationResult 評(píng)估結(jié)果 ? ? ?*/ ? ? default void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) { } ? ? /** ? ? ?* 運(yùn)行時(shí)異常導(dǎo)致條件評(píng)估錯(cuò)誤時(shí)觸發(fā) ? ? ?* ? ? ?* @param rule 評(píng)估之后的規(guī)則 ? ? ?* @param facts 評(píng)估時(shí)的已知事實(shí) ? ? ?* @param exception 條件評(píng)估時(shí)發(fā)生的異常 ? ? ?*/ ? ? default void onEvaluationError(Rule rule, Facts facts, Exception exception) { } ? ? /** ? ? ?* 在規(guī)則操作執(zhí)行之前觸發(fā)。 ? ? ?* ? ? ?* @param rule 當(dāng)前的規(guī)則 ? ? ?* @param facts 執(zhí)行規(guī)則操作時(shí)的已知事實(shí) ? ? ?*/ ? ? default void beforeExecute(Rule rule, Facts facts) { } ? ? /** ? ? ?* 在規(guī)則操作成功執(zhí)行之后觸發(fā) ? ? ?* ? ? ?* @param rule t當(dāng)前的規(guī)則 ? ? ?* @param facts 執(zhí)行規(guī)則操作時(shí)的已知事實(shí) ? ? ?*/ ? ? default void onSuccess(Rule rule, Facts facts) { } ? ? /** ? ? ?* 在規(guī)則操作執(zhí)行失敗時(shí)觸發(fā) ? ? ?* ? ? ?* @param rule 當(dāng)前的規(guī)則 ? ? ?* @param facts 執(zhí)行規(guī)則操作時(shí)的已知事實(shí) ? ? ?* @param exception 執(zhí)行規(guī)則操作時(shí)發(fā)生的異常 ? ? ?*/ ? ? default void onFailure(Rule rule, Facts facts, Exception exception) { } }
可以實(shí)現(xiàn)這個(gè)接口來提供自定義行為,以便在每個(gè)規(guī)則之前/之后執(zhí)行。要注冊(cè)監(jiān)聽器,請(qǐng)使用以下代碼段:
DefaultRulesEngine rulesEngine = new DefaultRulesEngine(); rulesEngine.registerRuleListener(myRuleListener);
可以注冊(cè)任意數(shù)量的偵聽器,它們將按照注冊(cè)順序執(zhí)行。
注意:當(dāng)使用組合規(guī)則時(shí),監(jiān)聽器是圍繞組合規(guī)則調(diào)用的。
定義規(guī)則引擎監(jiān)聽器
可以通過RulesEngineListener API來監(jiān)聽規(guī)則引擎的執(zhí)行事件:
public interface RulesEngineListener { ? ? /** ? ? ?* 在執(zhí)行規(guī)則集之前觸發(fā) ? ? ?* ? ? ?* @param rules 要觸發(fā)的規(guī)則集 ? ? ?* @param facts 觸發(fā)規(guī)則前的事實(shí) ? ? ?*/ ? ? default void beforeEvaluate(Rules rules, Facts facts) { } ? ? /** ? ? ?* 在執(zhí)行規(guī)則集之后觸發(fā) ? ? ?* ? ? ?* @param rules 要觸發(fā)的規(guī)則集 ? ? ?* @param facts 觸發(fā)規(guī)則前的事實(shí) ? ? ?*/ ? ? default void afterExecute(Rules rules, Facts facts) { } }
RulesEngineListener允許我們?cè)谟|發(fā)整個(gè)規(guī)則集之前/之后提供自定義行為。可以使用如下方式注冊(cè)監(jiān)聽器。
DefaultRulesEngine rulesEngine = new DefaultRulesEngine(); rulesEngine.registerRulesEngineListener(myRulesEngineListener);
可以注冊(cè)任意數(shù)量的監(jiān)聽器,它們將按照注冊(cè)順序執(zhí)行。
表達(dá)式語(yǔ)言(EL)支持
Easy Rules支持用MVEL、SpEL和JEXL定義規(guī)則。
EL提供者注意事項(xiàng)
EL提供者在行為上有一些區(qū)別。例如,當(dāng)一個(gè)事實(shí)在條件中缺失時(shí),MVEL拋出一個(gè)異常,而SpEL將忽略它并返回false。因此,在選擇Easy Rules使用哪個(gè)EL之前,你應(yīng)該了解這些差異。
通過編程的方式定義規(guī)則
條件、動(dòng)作和規(guī)則分別由MVELCondition/SpELCondition/JexlCondition、MVELAction/SpELAction/JexlAction和MVELRule/SpELRule/JexlRule類表示。下面是一個(gè)使用MVEL定義規(guī)則的例子:
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);");
通過規(guī)則描述文件定義規(guī)則
可以使用規(guī)則描述文件定義規(guī)則,使用MVELRuleFactory/SpELRuleFactory/JexlRuleFactory來從描述符文件創(chuàng)建規(guī)則。下面是一個(gè)在alcohol-rule.yml中以YAML格式定義的MVEL規(guī)則示例:
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"));
還可以使用一個(gè)文件創(chuàng)建多個(gè)規(guī)則。
--- 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!");"
可以使用如下方式將這些規(guī)則加載到rules對(duì)象中。
MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader()); Rules rules = ruleFactory.createRules(new FileReader("rules.yml"));
Easy Rules還支持從JSON描述符加載規(guī)則。具體參考文檔,這里不做展開。
規(guī)則定義中的錯(cuò)誤處理
關(guān)于條件中不正確表達(dá)式的引擎行為
對(duì)于條件求值過程中可能發(fā)生的任何運(yùn)行時(shí)異常(丟失事實(shí)、表達(dá)式中輸入錯(cuò)誤等),引擎將記錄一個(gè)警告,并認(rèn)為條件求值為false??梢允褂肦uleListener#onEvaluationError來監(jiān)聽評(píng)估錯(cuò)誤。
關(guān)于操作中不正確表達(dá)式的引擎行為
對(duì)于任何在執(zhí)行操作時(shí)可能發(fā)生的運(yùn)行時(shí)異常(丟失事實(shí)、表達(dá)式中輸入錯(cuò)誤等),該操作將不會(huì)執(zhí)行,引擎將記錄一個(gè)錯(cuò)誤??梢允褂肦uleListener#onFailure來監(jiān)聽操作執(zhí)行異常。當(dāng)一個(gè)規(guī)則失敗時(shí),引擎將移動(dòng)到下一個(gè)規(guī)則,除非設(shè)置了skipOnFirstFailedRule參數(shù)。
實(shí)際栗子
本栗子使用Easy Rules實(shí)現(xiàn)FizzBuzz應(yīng)用程序。FizzBuzz是一個(gè)簡(jiǎn)單的應(yīng)用程序,需要從1數(shù)到100,并且:
- 如果數(shù)字是5的倍數(shù),則打印“fizz”
- 如果數(shù)字是7的倍數(shù),請(qǐng)打印“buzz”
- 如果數(shù)字是5和7的倍數(shù),請(qǐng)打印“fizzbuzz”
- 否則打印數(shù)字本身
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(); } }
我們將為每個(gè)需求編寫一條規(guī)則:
@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; ? ? } }
以下是對(duì)這些規(guī)則的一些解釋:
- FizzRule和BuzzRule很簡(jiǎn)單,它們會(huì)檢查輸入是5的倍數(shù)還是7的倍數(shù),然后打印結(jié)果。
- FizzBuzzRule是一個(gè)組合規(guī)則。通過FizzRule和BuzzRule創(chuàng)建。基類選擇為UnitRuleGroup,要么滿足并應(yīng)用這兩個(gè)規(guī)則,要么什么都不應(yīng)用。
- NonFizzBuzzRule是既不是5的倍數(shù)也不是7的倍數(shù)時(shí)的規(guī)則。
請(qǐng)注意,我們已經(jīng)設(shè)置了優(yōu)先級(jí),因此規(guī)則的觸發(fā)順序與Java示例中的示例相同。
然后,我們必須將這些規(guī)則注冊(cè)到一個(gè)規(guī)則集中,并使用一個(gè)規(guī)則引擎來觸發(fā)它們:
public class FizzBuzzWithEasyRules { ? ? public static void main(String[] args) { ? ? ? ? // 創(chuàng)建規(guī)則引擎 ? ? ? ? RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(true); ? ? ? ? RulesEngine fizzBuzzEngine = new DefaultRulesEngine(parameters); ? ? ? ? // 創(chuàng)建規(guī)則 ? ? ? ? Rules rules = new Rules(); ? ? ? ? rules.register(new FizzRule()); ? ? ? ? rules.register(new BuzzRule()); ? ? ? ? rules.register(new FizzBuzzRule(new FizzRule(), new BuzzRule())); ? ? ? ? rules.register(new NonFizzBuzzRule()); ? ? ? ? // 觸發(fā)規(guī)則 ? ? ? ? Facts facts = new Facts(); ? ? ? ? for (int i = 1; i <= 100; i++) { ? ? ? ? ? ? facts.put("number", i); ? ? ? ? ? ? fizzBuzzEngine.fire(rules, facts); ? ? ? ? ? ? System.out.println(); ? ? ? ? } ? ? } }
注意,我們已經(jīng)設(shè)置了skipOnFirstAppliedRule參數(shù),以便在成功應(yīng)用規(guī)則時(shí)跳過后續(xù)的規(guī)則。
到此這篇關(guān)于Java規(guī)則引擎easy-rules詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Java規(guī)則引擎easy-rules詳細(xì)介紹內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java語(yǔ)言實(shí)現(xiàn)最大堆代碼示例
這篇文章主要介紹了Java語(yǔ)言實(shí)現(xiàn)最大堆代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。2017-12-12詳解Java中的reactive stream協(xié)議
Stream大家應(yīng)該都很熟悉了,java8中為所有的集合類都引入了Stream的概念。優(yōu)雅的鏈?zhǔn)讲僮?,流式處理邏輯,相信用過的人都會(huì)愛不釋手。本文將詳細(xì)介紹Java中的reactive stream協(xié)議。2021-06-06MyBatis動(dòng)態(tài)Sql之if標(biāo)簽的用法詳解
這篇文章主要介紹了MyBatis動(dòng)態(tài)Sql之if標(biāo)簽的用法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-07-07解決Spring Boot 多模塊注入訪問不到j(luò)ar包中的Bean問題
這篇文章主要介紹了解決Spring Boot 多模塊注入訪問不到j(luò)ar包中的Bean問題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09Springboot 讀取自定義pro文件注入static靜態(tài)變量方式
這篇文章主要介紹了Springboot 讀取自定義pro文件注入static靜態(tài)變量方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07JDK9對(duì)String字符串的新一輪優(yōu)化
這篇文章主要介紹了JDK9對(duì)String字符串的新一輪優(yōu)化,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03mybatis批量添加,批量更新之前如何判斷是否已經(jīng)存在
這篇文章主要介紹了mybatis批量添加,批量更新之前如何判斷是否已經(jīng)存在,具有很好的參考價(jià)值,希望對(duì)的有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08