目录
当下的规则引擎选择非常多,例如 EasyRule、Aviator 、QLExpress、Drools等;前面的文章也重点分析了Aviator框架源码,相关文章见:
本篇对另一种常用的规则引擎框架EasyRule进行源码分析;
EasyRule作为一种小而美的轻量级的规则引擎开源框架,从实际业务规则应用场景出发,抽象出了条件(Condition)、动作(Action)、规则(Rule)数据模型,并通过规则引擎(RulesEngine)完成规则的调度执行。
EasyRule的主要特性归纳如下:
轻量级框架和易于学习的API
基于POJO的开发与注解的编程模型
定义抽象的业务规则并轻松应用它们
支持从简单规则创建组合规则的能力
支持使用表达式语言(如MVEL和SpEL)定义规则的能力
本文对EasyRule归纳性的描述不再过多赘述,下面主要着重对EasyRule框架源码进行分析;
附:EasyRule引擎规则创建过程的源码分析详见:
EasyRule源码大家可以在github上找到:Github之EasyRule
这里选取源码中Tutorial模块中的例子进行说明:
- @Rule(name = "weather rule", description = "if it rains then take an umbrella")
- public class WeatherRule {
-
- @Condition
- public boolean itRains(@Fact("rain") boolean rain) {
- return rain;
- }
-
- @Action
- public void takeAnUmbrella() {
- System.out.println("It rains, take an umbrella!");
- }
- }
这里通过注解模式对业务规则进行了定义:
- @Rule:表明该类为一个规则类
- @Condition:表明规则类中的方法作为规则条件
- @Fact:作为条件中的传参
- @Action:作为规则条件命中之后具体执行的动作
完成规则类的定义后,通过启动测试验证类,验证规则的执行情况:
- public class Launcher {
-
- public static void main(String[] args) {
- // define facts
- Facts facts = new Facts();
- facts.put("rain", true);
-
- // define rules
- WeatherRule weatherRule = new WeatherRule();
- Rules rules = new Rules();
- rules.register(weatherRule);
-
- // fire rules on known facts
- RulesEngine rulesEngine = new DefaultRulesEngine();
- rulesEngine.fire(rules, facts);
- }
-
- }
上述的启动类中,主要包含3部分:
1)定义并初始化Facts
2)定义并注册规则
3)定义规则引擎,并进行规则调度
下面分别对这3部分进行分析
Fact&Facts设计目标为EasyRule的传参对象类,源码比较简单易懂,具体如下:
- public class Fact
{ -
- private final String name;
- private final T value;
-
- /**
- * Create a new fact.
- * @param name of the fact
- * @param value of the fact
- */
- public Fact(String name, T value) {
- Objects.requireNonNull(name, "name must not be null");
- Objects.requireNonNull(value, "value must not be null");
- this.name = name;
- this.value = value;
- }
-
- /**
- * Get the fact name.
- * @return fact name
- */
- public String getName() {
- return name;
- }
-
- /**
- * Get the fact value.
- * @return fact value
- */
- public T getValue() {
- return value;
- }
-
- @Override
- public String toString() {
- return "Fact{" +
- "name='" + name + '\'' +
- ", value=" + value +
- '}';
- }
-
- /*
- * The Facts API represents a namespace for facts where each fact has a unique name.
- * Hence, equals/hashcode are deliberately calculated only on the fact name.
- */
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Fact> fact = (Fact>) o;
- return name.equals(fact.name);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(name);
- }
- }
- /**
- * This class encapsulates a set of facts and represents a facts namespace.
- * Facts have unique names within a
Facts object. - *
- * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
- */
- public class Facts implements Iterable
> { -
- private final Set
> facts = new HashSet<>(); -
- /**
- * Add a fact, replacing any fact with the same name.
- *
- * @param name of the fact to add, must not be null
- * @param value of the fact to add, must not be null
- */
- public
void put(String name, T value) { - Objects.requireNonNull(name, "fact name must not be null");
- Objects.requireNonNull(value, "fact value must not be null");
- Fact> retrievedFact = getFact(name);
- if (retrievedFact != null) {
- remove(retrievedFact);
- }
- add(new Fact<>(name, value));
- }
-
- /**
- * Add a fact, replacing any fact with the same name.
- *
- * @param fact to add, must not be null
- */
- public
void add(Fact fact) { - Objects.requireNonNull(fact, "fact must not be null");
- Fact> retrievedFact = getFact(fact.getName());
- if (retrievedFact != null) {
- remove(retrievedFact);
- }
- facts.add(fact);
- }
-
- /**
- * Remove a fact by name.
- *
- * @param factName name of the fact to remove, must not be null
- */
- public void remove(String factName) {
- Objects.requireNonNull(factName, "fact name must not be null");
- Fact> fact = getFact(factName);
- if (fact != null) {
- remove(fact);
- }
- }
-
- /**
- * Remove a fact.
- *
- * @param fact to remove, must not be null
- */
- public
void remove(Fact fact) { - Objects.requireNonNull(fact, "fact must not be null");
- facts.remove(fact);
- }
-
- /**
- * Get the value of a fact by its name. This is a convenience method provided
- * as a short version of {@code getFact(factName).getValue()}.
- *
- * @param factName name of the fact, must not be null
- * @param
type of the fact's value - * @return the value of the fact having the given name, or null if there is
- * no fact with the given name
- */
- @SuppressWarnings("unchecked")
- public
T get(String factName) { - Objects.requireNonNull(factName, "fact name must not be null");
- Fact> fact = getFact(factName);
- if (fact != null) {
- return (T) fact.getValue();
- }
- return null;
- }
-
- /**
- * Get a fact by name.
- *
- * @param factName name of the fact, must not be null
- * @return the fact having the given name, or null if there is no fact with the given name
- */
- public Fact> getFact(String factName) {
- Objects.requireNonNull(factName, "fact name must not be null");
- return facts.stream()
- .filter(fact -> fact.getName().equals(factName))
- .findFirst()
- .orElse(null);
- }
-
- /**
- * Return a copy of the facts as a map. It is not intended to manipulate
- * facts outside of the rules engine (aka other than manipulating them through rules).
- *
- * @return a copy of the current facts as a {@link HashMap}
- */
- public Map
asMap() { - Map
map = new HashMap<>(); - for (Fact> fact : facts) {
- map.put(fact.getName(), fact.getValue());
- }
- return map;
- }
-
- /**
- * Return an iterator on the set of facts. It is not intended to remove
- * facts using this iterator outside of the rules engine (aka other than doing it through rules)
- *
- * @return an iterator on the set of facts
- */
- @Override
- public Iterator
> iterator() { - return facts.iterator();
- }
-
- /**
- * Clear facts.
- */
- public void clear() {
- facts.clear();
- }
-
- @Override
- public String toString() {
- Iterator
> iterator = facts.iterator(); - StringBuilder stringBuilder = new StringBuilder("[");
- while (iterator.hasNext()) {
- stringBuilder.append(iterator.next().toString());
- if (iterator.hasNext()) {
- stringBuilder.append(",");
- }
- }
- stringBuilder.append("]");
- return stringBuilder.toString();
- }
- }
Fact:封装了对象名称到实际对象引用的映射
Facts:对Fact进一步封装为Set集合,以及对应的集合操作
规则Rule整体类图如下:

BasicRule:Rule接口的基础实现类,管理规则名称、描述和优先级
DefaultRule:默认规则实现类,包含Condition和多个Action
SpELRule&MVELRule&JexlRule:支持SpEL、MVEL、Jexl表达式定义的Condition和Action
CompositeRule:组合规则,对多个规则组合管理
ActivationRuleGroup&ConditionalRuleGroup&UnitRuleGroup:封装不同的组合规则管理策略
Rules:负责规则的注册管理,其中完成了规则的代理类创建过程
这里对Rule接口定义进行展开,从顶层接口中描述了规则包含的行为,主要包含了规则的名称、描述、优先级属性定义以及规则评估和执行方法:
- /**
- * Abstraction for a rule that can be fired by a rules engine.
- *
- * Rules are registered in a namespace of rule of type {@link Rules}
- * in which they must have a unique name.
- *
- * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
- */
- public interface Rule extends Comparable
{ -
- /**
- * Default rule name.
- */
- String DEFAULT_NAME = "rule";
-
- /**
- * Default rule description.
- */
- String DEFAULT_DESCRIPTION = "description";
-
- /**
- * Default rule priority.
- */
- int DEFAULT_PRIORITY = Integer.MAX_VALUE - 1;
-
- /**
- * Getter for rule name.
- * @return the rule name
- */
- default String getName() {
- return DEFAULT_NAME;
- }
-
- /**
- * Getter for rule description.
- * @return rule description
- */
- default String getDescription() {
- return DEFAULT_DESCRIPTION;
- }
-
- /**
- * Getter for rule priority.
- * @return rule priority
- */
- default int getPriority() {
- return DEFAULT_PRIORITY;
- }
-
- /**
- * This method implements the rule's condition(s).
- * Implementations should handle any runtime exception and return true/false accordingly
- *
- * @return true if the rule should be applied given the provided facts, false otherwise
- */
- boolean evaluate(Facts facts);
-
- /**
- * This method implements the rule's action(s).
- * @throws Exception thrown if an exception occurs when performing action(s)
- */
- void execute(Facts facts) throws Exception;
-
- }
规则类定义完成后,需要对规则进行注册;Rules实现了该功能,通过调用register和unregister方法完成规则的注册管理;
- /**
- * This class encapsulates a set of rules and represents a rules namespace.
- * Rules must have a unique name within a rules namespace.
- *
- * Rules will be compared to each other based on {@link Rule#compareTo(Object)}
- * method, so {@link Rule}'s implementations are expected to correctly implement
- * {@code compareTo} to ensure unique rule names within a single namespace.
- *
- * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
- */
- public class Rules implements Iterable
{ -
- private Set
rules = new TreeSet<>(); -
- /**
- * Create a new {@link Rules} object.
- *
- * @param rules to register
- */
- public Rules(Set
rules) { - this.rules = new TreeSet<>(rules);
- }
-
- /**
- * Create a new {@link Rules} object.
- *
- * @param rules to register
- */
- public Rules(Rule... rules) {
- Collections.addAll(this.rules, rules);
- }
-
- /**
- * Create a new {@link Rules} object.
- *
- * @param rules to register
- */
- public Rules(Object... rules) {
- this.register(rules);
- }
-
- /**
- * Register one or more new rules.
- *
- * @param rules to register, must not be null
- */
- public void register(Object... rules) {
- Objects.requireNonNull(rules);
- for (Object rule : rules) {
- Objects.requireNonNull(rule);
- this.rules.add(RuleProxy.asRule(rule));
- }
- }
-
- /**
- * Unregister one or more rules.
- *
- * @param rules to unregister, must not be null
- */
- public void unregister(Object... rules) {
- Objects.requireNonNull(rules);
- for (Object rule : rules) {
- Objects.requireNonNull(rule);
- this.rules.remove(RuleProxy.asRule(rule));
- }
- }
-
- /**
- * Unregister a rule by name.
- *
- * @param ruleName name of the rule to unregister, must not be null
- */
- public void unregister(final String ruleName) {
- Objects.requireNonNull(ruleName);
- Rule rule = findRuleByName(ruleName);
- if (rule != null) {
- unregister(rule);
- }
- }
-
- /**
- * Check if the rule set is empty.
- *
- * @return true if the rule set is empty, false otherwise
- */
- public boolean isEmpty() {
- return rules.isEmpty();
- }
-
- /**
- * Clear rules.
- */
- public void clear() {
- rules.clear();
- }
-
- /**
- * Return how many rules are currently registered.
- *
- * @return the number of rules currently registered
- */
- public int size() {
- return rules.size();
- }
-
- /**
- * Return an iterator on the rules set. It is not intended to remove rules
- * using this iterator.
- * @return an iterator on the rules set
- */
- @Override
- public Iterator
iterator() { - return rules.iterator();
- }
-
- private Rule findRuleByName(String ruleName) {
- return rules.stream()
- .filter(rule -> rule.getName().equalsIgnoreCase(ruleName))
- .findFirst()
- .orElse(null);
- }
- }
在上面规则注册的方法中,通过调用RuleProxy.asRule(rule)完成规则的代理,实际注册的是规则的代理类,下面重点剖析下代理类的构造过程:
- /**
- * Main class to create rule proxies from annotated objects.
- *
- * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
- */
- public class RuleProxy implements InvocationHandler {
-
- private final Object target;
- private String name;
- private String description;
- private Integer priority;
- private Method[] methods;
- private Method conditionMethod;
- private Set
actionMethods; - private Method compareToMethod;
- private Method toStringMethod;
- private org.jeasy.rules.annotation.Rule annotation;
-
- private static final RuleDefinitionValidator ruleDefinitionValidator = new RuleDefinitionValidator();
- private static final Logger LOGGER = LoggerFactory.getLogger(RuleProxy.class);
-
- /**
- * Makes the rule object implement the {@link Rule} interface.
- *
- * @param rule the annotated rule object.
- * @return a proxy that implements the {@link Rule} interface.
- */
- public static Rule asRule(final Object rule) {
- Rule result;
- if (rule instanceof Rule) {
- result = (Rule) rule;
- } else {
- ruleDefinitionValidator.validateRuleDefinition(rule);
- result = (Rule) Proxy.newProxyInstance(
- Rule.class.getClassLoader(),
- new Class[]{Rule.class, Comparable.class},
- new RuleProxy(rule));
- }
- return result;
- }
-
- private RuleProxy(final Object target) {
- this.target = target;
- }
-
- @Override
- public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
- String methodName = method.getName();
- switch (methodName) {
- case "getName":
- return getRuleName();
- case "getDescription":
- return getRuleDescription();
- case "getPriority":
- return getRulePriority();
- case "compareTo":
- return compareToMethod(args);
- case "evaluate":
- return evaluateMethod(args);
- case "execute":
- return executeMethod(args);
- case "equals":
- return equalsMethod(args);
- case "hashCode":
- return hashCodeMethod();
- case "toString":
- return toStringMethod();
- default:
- return null;
- }
- }
-
- private Object evaluateMethod(final Object[] args) throws IllegalAccessException, InvocationTargetException {
- Facts facts = (Facts) args[0];
- Method conditionMethod = getConditionMethod();
- try {
- List
- return conditionMethod.invoke(target, actualParameters.toArray()); // validated upfront
- } catch (NoSuchFactException e) {
- LOGGER.warn("Rule '{}' has been evaluated to false due to a declared but missing fact '{}' in {}",
- getTargetClass().getName(), e.getMissingFact(), facts);
- return false;
- } catch (IllegalArgumentException e) {
- LOGGER.warn("Types of injected facts in method '{}' in rule '{}' do not match parameters types",
- conditionMethod.getName(), getTargetClass().getName(), e);
- return false;
- }
- }
-
- private Object executeMethod(final Object[] args) throws IllegalAccessException, InvocationTargetException {
- Facts facts = (Facts) args[0];
- for (ActionMethodOrderBean actionMethodBean : getActionMethodBeans()) {
- Method actionMethod = actionMethodBean.getMethod();
- List
- actionMethod.invoke(target, actualParameters.toArray());
- }
- return null;
- }
-
- private Object compareToMethod(final Object[] args) throws Exception {
- Method compareToMethod = getCompareToMethod();
- Object otherRule = args[0]; // validated upfront
- if (compareToMethod != null && Proxy.isProxyClass(otherRule.getClass())) {
- if (compareToMethod.getParameters().length != 1) {
- throw new IllegalArgumentException("compareTo method must have a single argument");
- }
- RuleProxy ruleProxy = (RuleProxy) Proxy.getInvocationHandler(otherRule);
- return compareToMethod.invoke(target, ruleProxy.getTarget());
- } else {
- return compareTo((Rule) otherRule);
- }
- }
-
- private List
- List
- Annotation[][] parameterAnnotations = method.getParameterAnnotations();
- for (Annotation[] annotations : parameterAnnotations) {
- if (annotations.length == 1) {
- String factName = ((Fact) (annotations[0])).value(); //validated upfront.
- Object fact = facts.get(factName);
- if (fact == null && !facts.asMap().containsKey(factName)) {
- throw new NoSuchFactException(format("No fact named '%s' found in known facts: %n%s", factName, facts), factName);
- }
- actualParameters.add(fact);
- } else {
- actualParameters.add(facts); //validated upfront, there may be only one parameter not annotated and which is of type Facts.class
- }
- }
- return actualParameters;
- }
-
- private boolean equalsMethod(final Object[] args) throws Exception {
- if (!(args[0] instanceof Rule)) {
- return false;
- }
- Rule otherRule = (Rule) args[0];
- int otherPriority = otherRule.getPriority();
- int priority = getRulePriority();
- if (priority != otherPriority) {
- return false;
- }
- String otherName = otherRule.getName();
- String name = getRuleName();
- if (!name.equals(otherName)) {
- return false;
- }
- String otherDescription = otherRule.getDescription();
- String description = getRuleDescription();
- return Objects.equals(description, otherDescription);
- }
-
- private int hashCodeMethod() throws Exception {
- int result = getRuleName().hashCode();
- int priority = getRulePriority();
- String description = getRuleDescription();
- result = 31 * result + (description != null ? description.hashCode() : 0);
- result = 31 * result + priority;
- return result;
- }
-
- private Method getToStringMethod() {
- if (this.toStringMethod == null) {
- Method[] methods = getMethods();
- for (Method method : methods) {
- if ("toString".equals(method.getName())) {
- this.toStringMethod = method;
- return this.toStringMethod;
- }
- }
- }
- return this.toStringMethod;
- }
-
- private String toStringMethod() throws Exception {
- Method toStringMethod = getToStringMethod();
- if (toStringMethod != null) {
- return (String) toStringMethod.invoke(target);
- } else {
- return getRuleName();
- }
- }
-
- private int compareTo(final Rule otherRule) throws Exception {
- int otherPriority = otherRule.getPriority();
- int priority = getRulePriority();
- if (priority < otherPriority) {
- return -1;
- } else if (priority > otherPriority) {
- return 1;
- } else {
- String otherName = otherRule.getName();
- String name = getRuleName();
- return name.compareTo(otherName);
- }
- }
-
- private int getRulePriority() throws Exception {
- if (this.priority == null) {
- int priority = Rule.DEFAULT_PRIORITY;
-
- org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
- if (rule.priority() != Rule.DEFAULT_PRIORITY) {
- priority = rule.priority();
- }
-
- Method[] methods = getMethods();
- for (Method method : methods) {
- if (method.isAnnotationPresent(Priority.class)) {
- priority = (int) method.invoke(target);
- break;
- }
- }
- this.priority = priority;
- }
- return this.priority;
- }
-
- private Method getConditionMethod() {
- if (this.conditionMethod == null) {
- Method[] methods = getMethods();
- for (Method method : methods) {
- if (method.isAnnotationPresent(Condition.class)) {
- this.conditionMethod = method;
- return this.conditionMethod;
- }
- }
- }
- return this.conditionMethod;
- }
-
- private Set
getActionMethodBeans() { - if (this.actionMethods == null) {
- this.actionMethods = new TreeSet<>();
- Method[] methods = getMethods();
- for (Method method : methods) {
- if (method.isAnnotationPresent(Action.class)) {
- Action actionAnnotation = method.getAnnotation(Action.class);
- int order = actionAnnotation.order();
- this.actionMethods.add(new ActionMethodOrderBean(method, order));
- }
- }
- }
- return this.actionMethods;
- }
-
- private Method getCompareToMethod() {
- if (this.compareToMethod == null) {
- Method[] methods = getMethods();
- for (Method method : methods) {
- if (method.getName().equals("compareTo")) {
- this.compareToMethod = method;
- return this.compareToMethod;
- }
- }
- }
- return this.compareToMethod;
- }
-
- private Method[] getMethods() {
- if (this.methods == null) {
- this.methods = getTargetClass().getMethods();
- }
- return this.methods;
- }
-
- private org.jeasy.rules.annotation.Rule getRuleAnnotation() {
- if (this.annotation == null) {
- this.annotation = Utils.findAnnotation(org.jeasy.rules.annotation.Rule.class, getTargetClass());
- }
- return this.annotation;
- }
-
- private String getRuleName() {
- if (this.name == null) {
- org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
- this.name = rule.name().equals(Rule.DEFAULT_NAME) ? getTargetClass().getSimpleName() : rule.name();
- }
- return this.name;
- }
-
- private String getRuleDescription() {
- if (this.description == null) {
- // Default description = "when " + conditionMethodName + " then " + comma separated actionMethodsNames
- StringBuilder description = new StringBuilder();
- appendConditionMethodName(description);
- appendActionMethodsNames(description);
- org.jeasy.rules.annotation.Rule rule = getRuleAnnotation();
- this.description = rule.description().equals(Rule.DEFAULT_DESCRIPTION) ? description.toString() : rule.description();
- }
- return this.description;
- }
-
- private void appendConditionMethodName(StringBuilder description) {
- Method method = getConditionMethod();
- if (method != null) {
- description.append("when ");
- description.append(method.getName());
- description.append(" then ");
- }
- }
-
- private void appendActionMethodsNames(StringBuilder description) {
- Iterator
iterator = getActionMethodBeans().iterator(); - while (iterator.hasNext()) {
- description.append(iterator.next().getMethod().getName());
- if (iterator.hasNext()) {
- description.append(",");
- }
- }
- }
-
- public Object getTarget() {
- return target;
- }
-
- private Class> getTargetClass() {
- return target.getClass();
- }
-
- }
RuleProxy通过实现InvocationHandler完成Interface-based JDK的动态代理过程,且在代理类中,对规则类的注解@Rule、@Condition、@Action、@Fact进行了解析并加以缓存;
规则引擎整体类图如下:

RulesEngine:定义顶层规则引擎接口功能
AbstractRulesEngine:抽象规则引擎实现类,封装规则拦截器和规则引擎拦截器注册管理逻辑
DefaultRulesEngine:默认规则引擎实现类
InferenceRulesEngine:委托规则引擎实现类,支持条件命中的条件下,多次触发规则执行
RulesEngineParameters:封装规则引擎配置参数
RuleListener:规则执行拦截器
RulesEngineListener:规则引擎执行拦截器
这里对RulesEngine接口定义展开如下,规则引擎顶层接口主要完成了
- /**
- * Rules engine interface.
- *
- * @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
- */
- public interface RulesEngine {
-
- /**
- * Return the rules engine parameters.
- *
- * @return The rules engine parameters
- */
- RulesEngineParameters getParameters();
-
- /**
- * Return the list of registered rule listeners.
- *
- * @return the list of registered rule listeners
- */
- default List
getRuleListeners() { - return Collections.emptyList();
- }
-
- /**
- * Return the list of registered rules engine listeners.
- *
- * @return the list of registered rules engine listeners
- */
- default List
getRulesEngineListeners() { - return Collections.emptyList();
- }
-
- /**
- * Fire all registered rules on given facts.
- */
- void fire(Rules rules, Facts facts);
-
- /**
- * Check rules without firing them.
- * @return a map with the result of evaluation of each rule
- */
- default Map
check(Rules rules, Facts facts) { - return Collections.emptyMap();
- }
- }
下面重点分析下fire方法的执行过程:
- @Override
- public void fire(Rules rules, Facts facts) {
- Objects.requireNonNull(rules, "Rules must not be null");
- Objects.requireNonNull(facts, "Facts must not be null");
- triggerListenersBeforeRules(rules, facts);
- doFire(rules, facts);
- triggerListenersAfterRules(rules, facts);
- }
-
- void doFire(Rules rules, Facts facts) {
- if (rules.isEmpty()) {
- LOGGER.warn("No rules registered! Nothing to apply");
- return;
- }
- logEngineParameters();
- log(rules);
- log(facts);
- LOGGER.debug("Rules evaluation started");
- for (Rule rule : rules) {
- final String name = rule.getName();
- final int priority = rule.getPriority();
- if (priority > parameters.getPriorityThreshold()) {
- LOGGER.debug("Rule priority threshold ({}) exceeded at rule '{}' with priority={}, next rules will be skipped",
- parameters.getPriorityThreshold(), name, priority);
- break;
- }
- if (!shouldBeEvaluated(rule, facts)) {
- LOGGER.debug("Rule '{}' has been skipped before being evaluated", name);
- continue;
- }
- boolean evaluationResult = false;
- try {
- evaluationResult = rule.evaluate(facts);
- } catch (RuntimeException exception) {
- LOGGER.error("Rule '" + name + "' evaluated with error", exception);
- triggerListenersOnEvaluationError(rule, facts, exception);
- // give the option to either skip next rules on evaluation error or continue by considering the evaluation error as false
- if (parameters.isSkipOnFirstNonTriggeredRule()) {
- LOGGER.debug("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");
- break;
- }
- }
- if (evaluationResult) {
- LOGGER.debug("Rule '{}' triggered", name);
- triggerListenersAfterEvaluate(rule, facts, true);
- try {
- triggerListenersBeforeExecute(rule, facts);
- rule.execute(facts);
- LOGGER.debug("Rule '{}' performed successfully", name);
- triggerListenersOnSuccess(rule, facts);
- if (parameters.isSkipOnFirstAppliedRule()) {
- LOGGER.debug("Next rules will be skipped since parameter skipOnFirstAppliedRule is set");
- break;
- }
- } catch (Exception exception) {
- LOGGER.error("Rule '" + name + "' performed with error", exception);
- triggerListenersOnFailure(rule, exception, facts);
- if (parameters.isSkipOnFirstFailedRule()) {
- LOGGER.debug("Next rules will be skipped since parameter skipOnFirstFailedRule is set");
- break;
- }
- }
- } else {
- LOGGER.debug("Rule '{}' has been evaluated to false, it has not been executed", name);
- triggerListenersAfterEvaluate(rule, facts, false);
- if (parameters.isSkipOnFirstNonTriggeredRule()) {
- LOGGER.debug("Next rules will be skipped since parameter skipOnFirstNonTriggeredRule is set");
- break;
- }
- }
- }
- }
如上fire方法主要的执行步骤为:
1.在doFire方法执行前后,调用了规则引擎拦截器的逻辑
2.在doFire中,遍历执行所有注册的规则(执行命中条件evaluate以及命中后的执行方法execute)
3.在规则执行的前后,也横切了规则拦截器的拦截逻辑
至此,一个简单的EasyRule规则引擎就分析完成了,整体的源码架构也比较简单易懂、逻辑分明、简洁轻量。