• EasyRule源码:EasyRule框架源码分析


    目录

    1.简要介绍EasyRule

    2.从实例入手看EasyRule使用方法

    3.执行过程源码分析

    3.1 Fact&Facts

    3.2 规则定义和注册

    3.2.1 Rule接口

    3.2.2 规则注册管理

    3.2.3 创建规则代理

    3.3 规则引擎调度执行


    1.简要介绍EasyRule

    当下的规则引擎选择非常多,例如 EasyRule、Aviator 、QLExpress、Drools等;前面的文章也重点分析了Aviator框架源码,相关文章见:

    1.【精选】Aviator源码:从具体实例看Aviator属性语法糖源码分析(a.b.c)

    2. Aviator源码:Aviator表达式引擎执行过程源码分析

    本篇对另一种常用的规则引擎框架EasyRule进行源码分析

    EasyRule作为一种小而美的轻量级的规则引擎开源框架,从实际业务规则应用场景出发,抽象出了条件(Condition)、动作(Action)、规则(Rule)数据模型,并通过规则引擎(RulesEngine)完成规则的调度执行。

    EasyRule的主要特性归纳如下:

    • 轻量级框架和易于学习的API

    • 基于POJO的开发与注解的编程模型

    • 定义抽象的业务规则并轻松应用它们

    • 支持从简单规则创建组合规则的能力

    • 支持使用表达式语言(如MVEL和SpEL)定义规则的能力

    本文对EasyRule归纳性的描述不再过多赘述,下面主要着重对EasyRule框架源码进行分析;

    附:EasyRule引擎规则创建过程的源码分析详见:

    EasyRule源码:工厂方法模式之规则创建源码分析

    2.从实例入手看EasyRule使用方法

    EasyRule源码大家可以在github上找到:Github之EasyRule

    这里选取源码中Tutorial模块中的例子进行说明:

    1. @Rule(name = "weather rule", description = "if it rains then take an umbrella")
    2. public class WeatherRule {
    3. @Condition
    4. public boolean itRains(@Fact("rain") boolean rain) {
    5. return rain;
    6. }
    7. @Action
    8. public void takeAnUmbrella() {
    9. System.out.println("It rains, take an umbrella!");
    10. }
    11. }

    这里通过注解模式对业务规则进行了定义:

    1. @Rule:表明该类为一个规则类
    2. @Condition:表明规则类中的方法作为规则条件
    3. @Fact:作为条件中的传参
    4. @Action:作为规则条件命中之后具体执行的动作

    完成规则类的定义后,通过启动测试验证类,验证规则的执行情况:

    1. public class Launcher {
    2. public static void main(String[] args) {
    3. // define facts
    4. Facts facts = new Facts();
    5. facts.put("rain", true);
    6. // define rules
    7. WeatherRule weatherRule = new WeatherRule();
    8. Rules rules = new Rules();
    9. rules.register(weatherRule);
    10. // fire rules on known facts
    11. RulesEngine rulesEngine = new DefaultRulesEngine();
    12. rulesEngine.fire(rules, facts);
    13. }
    14. }

    3.执行过程源码分析

    上述的启动类中,主要包含3部分:

    1)定义并初始化Facts

    2)定义并注册规则

    3)定义规则引擎,并进行规则调度

    下面分别对这3部分进行分析

    3.1 Fact&Facts

    Fact&Facts设计目标为EasyRule的传参对象类,源码比较简单易懂,具体如下:

    1. public class Fact {
    2. private final String name;
    3. private final T value;
    4. /**
    5. * Create a new fact.
    6. * @param name of the fact
    7. * @param value of the fact
    8. */
    9. public Fact(String name, T value) {
    10. Objects.requireNonNull(name, "name must not be null");
    11. Objects.requireNonNull(value, "value must not be null");
    12. this.name = name;
    13. this.value = value;
    14. }
    15. /**
    16. * Get the fact name.
    17. * @return fact name
    18. */
    19. public String getName() {
    20. return name;
    21. }
    22. /**
    23. * Get the fact value.
    24. * @return fact value
    25. */
    26. public T getValue() {
    27. return value;
    28. }
    29. @Override
    30. public String toString() {
    31. return "Fact{" +
    32. "name='" + name + '\'' +
    33. ", value=" + value +
    34. '}';
    35. }
    36. /*
    37. * The Facts API represents a namespace for facts where each fact has a unique name.
    38. * Hence, equals/hashcode are deliberately calculated only on the fact name.
    39. */
    40. @Override
    41. public boolean equals(Object o) {
    42. if (this == o) return true;
    43. if (o == null || getClass() != o.getClass()) return false;
    44. Fact fact = (Fact) o;
    45. return name.equals(fact.name);
    46. }
    47. @Override
    48. public int hashCode() {
    49. return Objects.hash(name);
    50. }
    51. }
    1. /**
    2. * This class encapsulates a set of facts and represents a facts namespace.
    3. * Facts have unique names within a Facts object.
    4. *
    5. * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
    6. */
    7. public class Facts implements Iterable> {
    8. private final Set> facts = new HashSet<>();
    9. /**
    10. * Add a fact, replacing any fact with the same name.
    11. *
    12. * @param name of the fact to add, must not be null
    13. * @param value of the fact to add, must not be null
    14. */
    15. public void put(String name, T value) {
    16. Objects.requireNonNull(name, "fact name must not be null");
    17. Objects.requireNonNull(value, "fact value must not be null");
    18. Fact retrievedFact = getFact(name);
    19. if (retrievedFact != null) {
    20. remove(retrievedFact);
    21. }
    22. add(new Fact<>(name, value));
    23. }
    24. /**
    25. * Add a fact, replacing any fact with the same name.
    26. *
    27. * @param fact to add, must not be null
    28. */
    29. public void add(Fact fact) {
    30. Objects.requireNonNull(fact, "fact must not be null");
    31. Fact retrievedFact = getFact(fact.getName());
    32. if (retrievedFact != null) {
    33. remove(retrievedFact);
    34. }
    35. facts.add(fact);
    36. }
    37. /**
    38. * Remove a fact by name.
    39. *
    40. * @param factName name of the fact to remove, must not be null
    41. */
    42. public void remove(String factName) {
    43. Objects.requireNonNull(factName, "fact name must not be null");
    44. Fact fact = getFact(factName);
    45. if (fact != null) {
    46. remove(fact);
    47. }
    48. }
    49. /**
    50. * Remove a fact.
    51. *
    52. * @param fact to remove, must not be null
    53. */
    54. public void remove(Fact fact) {
    55. Objects.requireNonNull(fact, "fact must not be null");
    56. facts.remove(fact);
    57. }
    58. /**
    59. * Get the value of a fact by its name. This is a convenience method provided
    60. * as a short version of {@code getFact(factName).getValue()}.
    61. *
    62. * @param factName name of the fact, must not be null
    63. * @param type of the fact's value
    64. * @return the value of the fact having the given name, or null if there is
    65. * no fact with the given name
    66. */
    67. @SuppressWarnings("unchecked")
    68. public T get(String factName) {
    69. Objects.requireNonNull(factName, "fact name must not be null");
    70. Fact fact = getFact(factName);
    71. if (fact != null) {
    72. return (T) fact.getValue();
    73. }
    74. return null;
    75. }
    76. /**
    77. * Get a fact by name.
    78. *
    79. * @param factName name of the fact, must not be null
    80. * @return the fact having the given name, or null if there is no fact with the given name
    81. */
    82. public Fact getFact(String factName) {
    83. Objects.requireNonNull(factName, "fact name must not be null");
    84. return facts.stream()
    85. .filter(fact -> fact.getName().equals(factName))
    86. .findFirst()
    87. .orElse(null);
    88. }
    89. /**
    90. * Return a copy of the facts as a map. It is not intended to manipulate
    91. * facts outside of the rules engine (aka other than manipulating them through rules).
    92. *
    93. * @return a copy of the current facts as a {@link HashMap}
    94. */
    95. public Map asMap() {
    96. Map map = new HashMap<>();
    97. for (Fact fact : facts) {
    98. map.put(fact.getName(), fact.getValue());
    99. }
    100. return map;
    101. }
    102. /**
    103. * Return an iterator on the set of facts. It is not intended to remove
    104. * facts using this iterator outside of the rules engine (aka other than doing it through rules)
    105. *
    106. * @return an iterator on the set of facts
    107. */
    108. @Override
    109. public Iterator> iterator() {
    110. return facts.iterator();
    111. }
    112. /**
    113. * Clear facts.
    114. */
    115. public void clear() {
    116. facts.clear();
    117. }
    118. @Override
    119. public String toString() {
    120. Iterator> iterator = facts.iterator();
    121. StringBuilder stringBuilder = new StringBuilder("[");
    122. while (iterator.hasNext()) {
    123. stringBuilder.append(iterator.next().toString());
    124. if (iterator.hasNext()) {
    125. stringBuilder.append(",");
    126. }
    127. }
    128. stringBuilder.append("]");
    129. return stringBuilder.toString();
    130. }
    131. }

    Fact:封装了对象名称到实际对象引用的映射

    Facts:对Fact进一步封装为Set集合,以及对应的集合操作

    3.2 规则定义和注册

    3.2.1 Rule接口

    规则Rule整体类图如下:

    BasicRule:Rule接口的基础实现类,管理规则名称、描述和优先级

    DefaultRule:默认规则实现类,包含Condition和多个Action

    SpELRule&MVELRule&JexlRule:支持SpEL、MVEL、Jexl表达式定义的Condition和Action

    CompositeRule:组合规则,对多个规则组合管理

    ActivationRuleGroup&ConditionalRuleGroup&UnitRuleGroup:封装不同的组合规则管理策略

    Rules:负责规则的注册管理,其中完成了规则的代理类创建过程

    这里对Rule接口定义进行展开,从顶层接口中描述了规则包含的行为,主要包含了规则的名称、描述、优先级属性定义以及规则评估和执行方法:

    1. /**
    2. * Abstraction for a rule that can be fired by a rules engine.
    3. *
    4. * Rules are registered in a namespace of rule of type {@link Rules}
    5. * in which they must have a unique name.
    6. *
    7. * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
    8. */
    9. public interface Rule extends Comparable {
    10. /**
    11. * Default rule name.
    12. */
    13. String DEFAULT_NAME = "rule";
    14. /**
    15. * Default rule description.
    16. */
    17. String DEFAULT_DESCRIPTION = "description";
    18. /**
    19. * Default rule priority.
    20. */
    21. int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;
    22. /**
    23. * Getter for rule name.
    24. * @return the rule name
    25. */
    26. default String getName() {
    27. return DEFAULT_NAME;
    28. }
    29. /**
    30. * Getter for rule description.
    31. * @return rule description
    32. */
    33. default String getDescription() {
    34. return DEFAULT_DESCRIPTION;
    35. }
    36. /**
    37. * Getter for rule priority.
    38. * @return rule priority
    39. */
    40. default int getPriority() {
    41. return DEFAULT_PRIORITY;
    42. }
    43. /**
    44. * This method implements the rule's condition(s).
    45. * Implementations should handle any runtime exception and return true/false accordingly
    46. *
    47. * @return true if the rule should be applied given the provided facts, false otherwise
    48. */
    49. boolean evaluate(Facts facts);
    50. /**
    51. * This method implements the rule's action(s).
    52. * @throws Exception thrown if an exception occurs when performing action(s)
    53. */
    54. void execute(Facts facts) throws Exception;
    55. }

    3.2.2 规则注册管理

    规则类定义完成后,需要对规则进行注册;Rules实现了该功能,通过调用register和unregister方法完成规则的注册管理;

    1. /**
    2. * This class encapsulates a set of rules and represents a rules namespace.
    3. * Rules must have a unique name within a rules namespace.
    4. *
    5. * Rules will be compared to each other based on {@link Rule#compareTo(Object)}
    6. * method, so {@link Rule}'s implementations are expected to correctly implement
    7. * {@code compareTo} to ensure unique rule names within a single namespace.
    8. *
    9. * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
    10. */
    11. public class Rules implements Iterable {
    12. private Set rules = new TreeSet<>();
    13. /**
    14. * Create a new {@link Rules} object.
    15. *
    16. * @param rules to register
    17. */
    18. public Rules(Set rules) {
    19. this.rules = new TreeSet<>(rules);
    20. }
    21. /**
    22. * Create a new {@link Rules} object.
    23. *
    24. * @param rules to register
    25. */
    26. public Rules(Rule... rules) {
    27. Collections.addAll(this.rules, rules);
    28. }
    29. /**
    30. * Create a new {@link Rules} object.
    31. *
    32. * @param rules to register
    33. */
    34. public Rules(Object... rules) {
    35. this.register(rules);
    36. }
    37. /**
    38. * Register one or more new rules.
    39. *
    40. * @param rules to register, must not be null
    41. */
    42. public void register(Object... rules) {
    43. Objects.requireNonNull(rules);
    44. for (Object rule : rules) {
    45. Objects.requireNonNull(rule);
    46. this.rules.add(RuleProxy.asRule(rule));
    47. }
    48. }
    49. /**
    50. * Unregister one or more rules.
    51. *
    52. * @param rules to unregister, must not be null
    53. */
    54. public void unregister(Object... rules) {
    55. Objects.requireNonNull(rules);
    56. for (Object rule : rules) {
    57. Objects.requireNonNull(rule);
    58. this.rules.remove(RuleProxy.asRule(rule));
    59. }
    60. }
    61. /**
    62. * Unregister a rule by name.
    63. *
    64. * @param ruleName name of the rule to unregister, must not be null
    65. */
    66. public void unregister(final String ruleName) {
    67. Objects.requireNonNull(ruleName);
    68. Rule rule = findRuleByName(ruleName);
    69. if (rule != null) {
    70. unregister(rule);
    71. }
    72. }
    73. /**
    74. * Check if the rule set is empty.
    75. *
    76. * @return true if the rule set is empty, false otherwise
    77. */
    78. public boolean isEmpty() {
    79. return rules.isEmpty();
    80. }
    81. /**
    82. * Clear rules.
    83. */
    84. public void clear() {
    85. rules.clear();
    86. }
    87. /**
    88. * Return how many rules are currently registered.
    89. *
    90. * @return the number of rules currently registered
    91. */
    92. public int size() {
    93. return rules.size();
    94. }
    95. /**
    96. * Return an iterator on the rules set. It is not intended to remove rules
    97. * using this iterator.
    98. * @return an iterator on the rules set
    99. */
    100. @Override
    101. public Iterator iterator() {
    102. return rules.iterator();
    103. }
    104. private Rule findRuleByName(String ruleName) {
    105. return rules.stream()
    106. .filter(rule -> rule.getName().equalsIgnoreCase(ruleName))
    107. .findFirst()
    108. .orElse(null);
    109. }
    110. }

    3.2.3 创建规则代理

    在上面规则注册的方法中,通过调用RuleProxy.asRule(rule)完成规则的代理,实际注册的是规则的代理类,下面重点剖析下代理类的构造过程:

    1. /**
    2. * Main class to create rule proxies from annotated objects.
    3. *
    4. * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
    5. */
    6. public class RuleProxy implements InvocationHandler {
    7. private final Object target;
    8. private String name;
    9. private String description;
    10. private Integer priority;
    11. private Method[] methods;
    12. private Method conditionMethod;
    13. private Set actionMethods;
    14. private Method compareToMethod;
    15. private Method toStringMethod;
    16. private org.jeasy.rules.annotation.Rule annotation;
    17. private static final RuleDefinitionValidator ruleDefinitionValidator = new RuleDefinitionValidator();
    18. private static final Logger LOGGER = LoggerFactory.getLogger(RuleProxy.class);
    19. /**
    20. * Makes the rule object implement the {@link Rule} interface.
    21. *
    22. * @param rule the annotated rule object.
    23. * @return a proxy that implements the {@link Rule} interface.
    24. */
    25. public static Rule asRule(final Object rule) {
    26. Rule result;
    27. if (rule instanceof Rule) {
    28. result = (Rule) rule;
    29. } else {
    30. ruleDefinitionValidator.validateRuleDefinition(rule);
    31. result = (Rule) Proxy.newProxyInstance(
    32. Rule.class.getClassLoader(),
    33. new Class[]{Rule.class, Comparable.class},
    34. new RuleProxy(rule));
    35. }
    36. return result;
    37. }
    38. private RuleProxy(final Object target) {
    39. this.target = target;
    40. }
    41. @Override
    42. public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
    43. String methodName = method.getName();
    44. switch (methodName) {
    45. case "getName":
    46. return getRuleName();
    47. case "getDescription":
    48. return getRuleDescription();
    49. case "getPriority":
    50. return getRulePriority();
    51. case "compareTo":
    52. return compareToMethod(args);
    53. case "evaluate":
    54. return evaluateMethod(args);
    55. case "execute":
    56. return executeMethod(args);
    57. case "equals":
    58. return equalsMethod(args);
    59. case "hashCode":
    60. return hashCodeMethod();
    61. case "toString":
    62. return toStringMethod();
    63. default:
    64. return null;
    65. }
    66. }
    67. private Object evaluateMethod(final Object[] args) throws IllegalAccessException, InvocationTargetException {
    68. Facts facts = (Facts) args[0];
    69. Method conditionMethod = getConditionMethod();
    70. try {
    71. List actualParameters = getActualParameters(conditionMethod, facts);
    72. return conditionMethod.invoke(target, actualParameters.toArray()); // validated upfront
    73. } catch (NoSuchFactException e) {
    74. LOGGER.warn("Rule '{}' has been evaluated to false due to a declared but missing fact '{}' in {}",
    75. getTargetClass().getName(), e.getMissingFact(), facts);
    76. return false;
    77. } catch (IllegalArgumentException e) {
    78. LOGGER.warn("Types of injected facts in method '{}' in rule '{}' do not match parameters types",
    79. conditionMethod.getName(), getTargetClass().getName(), e);
    80. return false;
    81. }
    82. }
    83. private Object executeMethod(final Object[] args) throws IllegalAccessException, InvocationTargetException {
    84. Facts facts = (Facts) args[0];
    85. for (ActionMethodOrderBean actionMethodBean : getActionMethodBeans()) {
    86. Method actionMethod = actionMethodBean.getMethod();
    87. List actualParameters = getActualParameters(actionMethod, facts);
    88. actionMethod.invoke(target, actualParameters.toArray());
    89. }
    90. return null;
    91. }
    92. private Object compareToMethod(final Object[] args) throws Exception {
    93. Method compareToMethod = getCompareToMethod();
    94. Object otherRule = args[0]; // validated upfront
    95. if (compareToMethod != null && Proxy.isProxyClass(otherRule.getClass())) {
    96. if (compareToMethod.getParameters().length != 1) {
    97. throw new IllegalArgumentException("compareTo method must have a single argument");
    98. }
    99. RuleProxy ruleProxy = (RuleProxy) Proxy.getInvocationHandler(otherRule);
    100. return compareToMethod.invoke(target, ruleProxy.getTarget());
    101. } else {
    102. return compareTo((Rule) otherRule);
    103. }
    104. }
    105. private List getActualParameters(Method method, Facts facts) {
    106. List actualParameters = new ArrayList<>();
    107. Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    108. for (Annotation[] annotations : parameterAnnotations) {
    109. if (annotations.length == 1) {
    110. String factName = ((Fact) (annotations[0])).value(); //validated upfront.
    111. Object fact = facts.get(factName);
    112. if (fact == null && !facts.asMap().containsKey(factName)) {
    113. throw new NoSuchFactException(format("No fact named '%s' found in known facts: %n%s", factName, facts), factName);
    114. }
    115. actualParameters.add(fact);
    116. } else {
    117. actualParameters.add(facts); //validated upfront, there may be only one parameter not annotated and which is of type Facts.class
    118. }
    119. }
    120. return actualParameters;
    121. }
    122. private boolean equalsMethod(final Object[] args) throws Exception {
    123. if (!(args[0] instanceof Rule)) {
    124. return false;
    125. }
    126. Rule otherRule = (Rule) args[0];
    127. int otherPriority = otherRule.getPriority();
    128. int priority = getRulePriority();
    129. if (priority != otherPriority) {
    130. return false;
    131. }
    132. String otherName = otherRule.getName();
    133. String name = getRuleName();
    134. if (!name.equals(otherName)) {
    135. return false;
    136. }
    137. String otherDescription = otherRule.getDescription();
    138. String description = getRuleDescription();
    139. return Objects.equals(description, otherDescription);
    140. }
    141. private int hashCodeMethod() throws Exception {
    142. int result = getRuleName().hashCode();
    143. int priority = getRulePriority();
    144. String description = getRuleDescription();
    145. result = 31 * result + (description != null ? description.hashCode() : 0);
    146. result = 31 * result + priority;
    147. return result;
    148. }
    149. private Method getToStringMethod() {
    150. if (this.toStringMethod == null) {
    151. Method[] methods = getMethods();
    152. for (Method method : methods) {
    153. if ("toString".equals(method.getName())) {
    154. this.toStringMethod = method;
    155. return this.toStringMethod;
    156. }
    157. }
    158. }
    159. return this.toStringMethod;
    160. }
    161. private String toStringMethod() throws Exception {
    162. Method toStringMethod = getToStringMethod();
    163. if (toStringMethod != null) {
    164. return (String) toStringMethod.invoke(target);
    165. } else {
    166. return getRuleName();
    167. }
    168. }
    169. private int compareTo(final Rule otherRule) throws Exception {
    170. int otherPriority = otherRule.getPriority();
    171. int priority = getRulePriority();
    172. if (priority < otherPriority) {
    173. return -1;
    174. } else if (priority > otherPriority) {
    175. return 1;
    176. } else {
    177. String otherName = otherRule.getName();
    178. String name = getRuleName();
    179. return name.compareTo(otherName);
    180. }
    181. }
    182. private int getRulePriority() throws Exception {
    183. if (this.priority == null) {
    184. int priority = Rule.DEFAULT_PRIORITY;
    185. org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
    186. if (rule.priority() != Rule.DEFAULT_PRIORITY) {
    187. priority = rule.priority();
    188. }
    189. Method[] methods = getMethods();
    190. for (Method method : methods) {
    191. if (method.isAnnotationPresent(Priority.class)) {
    192. priority = (int) method.invoke(target);
    193. break;
    194. }
    195. }
    196. this.priority = priority;
    197. }
    198. return this.priority;
    199. }
    200. private Method getConditionMethod() {
    201. if (this.conditionMethod == null) {
    202. Method[] methods = getMethods();
    203. for (Method method : methods) {
    204. if (method.isAnnotationPresent(Condition.class)) {
    205. this.conditionMethod = method;
    206. return this.conditionMethod;
    207. }
    208. }
    209. }
    210. return this.conditionMethod;
    211. }
    212. private Set getActionMethodBeans() {
    213. if (this.actionMethods == null) {
    214. this.actionMethods = new TreeSet<>();
    215. Method[] methods = getMethods();
    216. for (Method method : methods) {
    217. if (method.isAnnotationPresent(Action.class)) {
    218. Action actionAnnotation = method.getAnnotation(Action.class);
    219. int order = actionAnnotation.order();
    220. this.actionMethods.add(new ActionMethodOrderBean(method, order));
    221. }
    222. }
    223. }
    224. return this.actionMethods;
    225. }
    226. private Method getCompareToMethod() {
    227. if (this.compareToMethod == null) {
    228. Method[] methods = getMethods();
    229. for (Method method : methods) {
    230. if (method.getName().equals("compareTo")) {
    231. this.compareToMethod = method;
    232. return this.compareToMethod;
    233. }
    234. }
    235. }
    236. return this.compareToMethod;
    237. }
    238. private Method[] getMethods() {
    239. if (this.methods == null) {
    240. this.methods = getTargetClass().getMethods();
    241. }
    242. return this.methods;
    243. }
    244. private org.jeasy.rules.annotation.Rule getRuleAnnotation() {
    245. if (this.annotation == null) {
    246. this.annotation = Utils.findAnnotation(org.jeasy.rules.annotation.Rule.class, getTargetClass());
    247. }
    248. return this.annotation;
    249. }
    250. private String getRuleName() {
    251. if (this.name == null) {
    252. org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
    253. this.name = rule.name().equals(Rule.DEFAULT_NAME) ? getTargetClass().getSimpleName() : rule.name();
    254. }
    255. return this.name;
    256. }
    257. private String getRuleDescription() {
    258. if (this.description == null) {
    259. // Default description = "when " + conditionMethodName + " then " + comma separated actionMethodsNames
    260. StringBuilder description = new StringBuilder();
    261. appendConditionMethodName(description);
    262. appendActionMethodsNames(description);
    263. org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
    264. this.description = rule.description().equals(Rule.DEFAULT_DESCRIPTION) ? description.toString() : rule.description();
    265. }
    266. return this.description;
    267. }
    268. private void appendConditionMethodName(StringBuilder description) {
    269. Method method = getConditionMethod();
    270. if (method != null) {
    271. description.append("when ");
    272. description.append(method.getName());
    273. description.append(" then ");
    274. }
    275. }
    276. private void appendActionMethodsNames(StringBuilder description) {
    277. Iterator iterator = getActionMethodBeans().iterator();
    278. while (iterator.hasNext()) {
    279. description.append(iterator.next().getMethod().getName());
    280. if (iterator.hasNext()) {
    281. description.append(",");
    282. }
    283. }
    284. }
    285. public Object getTarget() {
    286. return target;
    287. }
    288. private Class getTargetClass() {
    289. return target.getClass();
    290. }
    291. }
    292. RuleProxy通过实现InvocationHandler完成Interface-based JDK的动态代理过程,且在代理类中,对规则类的注解@Rule、@Condition、@Action、@Fact进行了解析并加以缓存;

      3.3 规则引擎调度执行

      规则引擎整体类图如下:

      RulesEngine:定义顶层规则引擎接口功能

      AbstractRulesEngine:抽象规则引擎实现类,封装规则拦截器和规则引擎拦截器注册管理逻辑

      DefaultRulesEngine:默认规则引擎实现类

      InferenceRulesEngine:委托规则引擎实现类,支持条件命中的条件下,多次触发规则执行

      RulesEngineParameters:封装规则引擎配置参数

      RuleListener:规则执行拦截器

      RulesEngineListener:规则引擎执行拦截器

       这里对RulesEngine接口定义展开如下,规则引擎顶层接口主要完成了

      • 规则拦截器和规则引擎拦截器管理
      • 配置参数管理
      • 调度触发功能(fire方法)
      1. /**
      2. * Rules engine interface.
      3. *
      4. * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
      5. */
      6. public interface RulesEngine {
      7. /**
      8. * Return the rules engine parameters.
      9. *
      10. * @return The rules engine parameters
      11. */
      12. RulesEngineParameters getParameters();
      13. /**
      14. * Return the list of registered rule listeners.
      15. *
      16. * @return the list of registered rule listeners
      17. */
      18. default List getRuleListeners() {
      19. return Collections.emptyList();
      20. }
      21. /**
      22. * Return the list of registered rules engine listeners.
      23. *
      24. * @return the list of registered rules engine listeners
      25. */
      26. default List getRulesEngineListeners() {
      27. return Collections.emptyList();
      28. }
      29. /**
      30. * Fire all registered rules on given facts.
      31. */
      32. void fire(Rules rules, Facts facts);
      33. /**
      34. * Check rules without firing them.
      35. * @return a map with the result of evaluation of each rule
      36. */
      37. default Map check(Rules rules, Facts facts) {
      38. return Collections.emptyMap();
      39. }
      40. }

      下面重点分析下fire方法的执行过程:

      1. @Override
      2. public void fire(Rules rules, Facts facts) {
      3. Objects.requireNonNull(rules, "Rules must not be null");
      4. Objects.requireNonNull(facts, "Facts must not be null");
      5. triggerListenersBeforeRules(rules, facts);
      6. doFire(rules, facts);
      7. triggerListenersAfterRules(rules, facts);
      8. }
      9. void doFire(Rules rules, Facts facts) {
      10. if (rules.isEmpty()) {
      11. LOGGER.warn("No rules registered! Nothing to apply");
      12. return;
      13. }
      14. logEngineParameters();
      15. log(rules);
      16. log(facts);
      17. LOGGER.debug("Rules evaluation started");
      18. for (Rule rule : rules) {
      19. final String name = rule.getName();
      20. final int priority = rule.getPriority();
      21. if (priority > parameters.getPriorityThreshold()) {
      22. LOGGER.debug("Rule priority threshold ({}) exceeded at rule '{}' with priority={}, next rules will be skipped",
      23. parameters.getPriorityThreshold(), name, priority);
      24. break;
      25. }
      26. if (!shouldBeEvaluated(rule, facts)) {
      27. LOGGER.debug("Rule '{}' has been skipped before being evaluated", name);
      28. continue;
      29. }
      30. boolean evaluationResult = false;
      31. try {
      32. evaluationResult = rule.evaluate(facts);
      33. } catch (RuntimeException exception) {
      34. LOGGER.error("Rule '" + name + "' evaluated with error", exception);
      35. triggerListenersOnEvaluationError(rule, facts, exception);
      36. // give the option to either skip next rules on evaluation error or continue by considering the evaluation error as false
      37. if (parameters.isSkipOnFirstNonTriggeredRule()) {
      38. LOGGER.debug("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");
      39. break;
      40. }
      41. }
      42. if (evaluationResult) {
      43. LOGGER.debug("Rule '{}' triggered", name);
      44. triggerListenersAfterEvaluate(rule, facts, true);
      45. try {
      46. triggerListenersBeforeExecute(rule, facts);
      47. rule.execute(facts);
      48. LOGGER.debug("Rule '{}' performed successfully", name);
      49. triggerListenersOnSuccess(rule, facts);
      50. if (parameters.isSkipOnFirstAppliedRule()) {
      51. LOGGER.debug("Next rules will be skipped since parameter skipOnFirstAppliedRule is set");
      52. break;
      53. }
      54. } catch (Exception exception) {
      55. LOGGER.error("Rule '" + name + "' performed with error", exception);
      56. triggerListenersOnFailure(rule, exception, facts);
      57. if (parameters.isSkipOnFirstFailedRule()) {
      58. LOGGER.debug("Next rules will be skipped since parameter skipOnFirstFailedRule is set");
      59. break;
      60. }
      61. }
      62. } else {
      63. LOGGER.debug("Rule '{}' has been evaluated to false, it has not been executed", name);
      64. triggerListenersAfterEvaluate(rule, facts, false);
      65. if (parameters.isSkipOnFirstNonTriggeredRule()) {
      66. LOGGER.debug("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");
      67. break;
      68. }
      69. }
      70. }
      71. }

      如上fire方法主要的执行步骤为:

      1.在doFire方法执行前后,调用了规则引擎拦截器的逻辑

      2.在doFire中,遍历执行所有注册的规则(执行命中条件evaluate以及命中后的执行方法execute)

      3.在规则执行的前后,也横切了规则拦截器的拦截逻辑

      至此,一个简单的EasyRule规则引擎就分析完成了,整体的源码架构也比较简单易懂、逻辑分明、简洁轻量。

    293. 相关阅读:
      【第十九篇】- Maven NetBeans
      centos7.6配置用户免密及权限
      前端-electron教程
      linux安装Chrome跑web自动化
      生产环境日志打印console.log内存溢出解决方法
      【JAVA进阶篇】时间与日期相关类
      React Fiber架构原理剖析
      九月组队学习计划!
      vue3.0——监听属性、Vue3生命周期函数、Teleport、属性传值、自定义事件、状态驱动的动态 CSS、注册组件、异步组件、占位组件 Suspense
      C++第二十二弹---vector深度剖析及模拟实现(下)
    294. 原文地址:https://blog.csdn.net/supzhili/article/details/133928478