目录
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、Zools等;前面的文章也重点分析了Aviator框架源码,相关文章见:
1.【精选】Aviator源码:从具体实例看Aviator属性语法糖源码分析(a.b.c)_aviator 变量语法糖_mumubili的博客-CSDN博客
2. Aviator源码:Aviator表达式引擎执行过程源码分析_表达式引擎aviator-CSDN博客
本篇对另一种常用的规则引擎框架EasyRule进行源码分析;
EasyRule作为一种小而美的轻量级的规则引擎开源框架,从实际业务规则应用场景出发,抽象出了条件(Condition)、动作(Action)、规则(Rule)数据模型,并通过规则引擎(RulesEngine)完成规则的调度执行。
EasyRule的主要特性归纳如下:
-
轻量级框架和易于学习的API
-
基于POJO的开发与注解的编程模型
-
定义抽象的业务规则并轻松应用它们
-
支持从简单规则创建组合规则的能力
-
支持使用表达式语言(如MVEL和SpEL)定义规则的能力
本文对EasyRule归纳性的描述不再过多赘述,下面主要着重对EasyRule框架源码进行分析;
2.从实例入手看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.执行过程源码分析
上述的启动类中,只要包含3部分:
1)定义并初始化Facts
2)定义并注册规则
3)定义规则引擎,并进行规则调度
下面分别对这3部分进行分析
3.1 Fact&Facts
Fact&Facts源码如下:
public class Fact<T> {
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 <code>Facts</code> object.
*
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*/
public class Facts implements Iterable<Fact<?>> {
private final Set<Fact<?>> 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 <T> 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 <T> void add(Fact<T> 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 <T> void remove(Fact<T> 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 <T> 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> 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<String, Object> asMap() {
Map<String, Object> 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<Fact<?>> iterator() {
return facts.iterator();
}
/**
* Clear facts.
*/
public void clear() {
facts.clear();
}
@Override
public String toString() {
Iterator<Fact<?>> 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集合,以及对应的集合操作
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接口定义如下,主要包含了规则的名称、描述、优先级等属性定义:
/**
* 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 <strong>unique</strong> name.
*
* @author Mahmoud Ben Hassine (mahmoud.benhassine@icloud.com)
*/
public interface Rule extends Comparable<Rule> {
/**
* 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).
* <strong>Implementations should handle any runtime exception and return true/false accordingly</strong>
*
* @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;
}
3.2.2 规则注册管理
对规则的注册是通过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<Rule> {
private Set<Rule> rules = new TreeSet<>();
/**
* Create a new {@link Rules} object.
*
* @param rules to register
*/
public Rules(Set<Rule> 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<Rule> iterator() {
return rules.iterator();
}
private Rule findRuleByName(String ruleName) {
return rules.stream()
.filter(rule -> rule.getName().equalsIgnoreCase(ruleName))
.findFirst()
.orElse(null);
}
}
3.2.3 创建规则代理
在上面规则注册的方法中,通过调用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<ActionMethodOrderBean> 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<Object> actualParameters = getActualParameters(conditionMethod, facts);
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<Object> actualParameters = getActualParameters(actionMethod, facts);
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<Object> getActualParameters(Method method, Facts facts) {
List<Object> actualParameters = new ArrayList<>();
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<ActionMethodOrderBean> 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<ActionMethodOrderBean> 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();
}
}
通过实现InvocationHandler完成Interface-based JDK的动态代理过程,且在代理类中,对规则类的注解@Rule、@Condition、@Action、@Fact进行了解析并加以缓存;
3.3 规则引擎调度执行
规则引擎整体类图:
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<RuleListener> getRuleListeners() {
return Collections.emptyList();
}
/**
* Return the list of registered rules engine listeners.
*
* @return the list of registered rules engine listeners
*/
default List<RulesEngineListener> 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<Rule, Boolean> 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中,遍历执行所有注册的规则
3.在规则执行的前后,也横切了规则拦截器的拦截逻辑