EasyRule源码:EasyRule框架源码分析

news2024/9/24 17:13:53

目录

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!");
    }
}

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

  1. @Rule:表明该类为一个规则类
  2. @Condition:表明规则类中的方法作为规则条件
  3. @Fact:作为条件中的传参
  4. @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.在规则执行的前后,也横切了规则拦截器的拦截逻辑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1111068.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PN8016 宽输出范围非隔离交直流转换芯片适用于非隔离的辅助电源

PN8016集成PFM控制器及800V高雪崩能力智能功率MOSFET&#xff0c;用于外围元器件极精简的小功率非隔离开关电源&#xff0c;输出电压可通过FB电阻调整。 PN8016内置800V高压启动与自供电模块&#xff0c;实现系统快速启动、超低待机、自供电功能。该芯片提供了完整的智能化保护…

Twitter正在测试年度订阅以进行推文和转推

导语&#xff1a;最近&#xff0c;Twitter&#xff08;现更名为X&#xff09;正在测试新的年度订阅服务&#xff0c;为未经验证的账户提供推文和转推等核心功能。这一举措是一个名为“Not A Bot”的测试计划的一部分&#xff0c;从本周二开始在新西兰和菲律宾用户中推出。通过设…

【MyBatis】mybatis工具类迭代

目录 MyBatis工具类的迭代 ThreadLocal使用 mybatis工具类终极版&#xff1a; MyBatis工具类的迭代 public class MyBatisUtil {//工具类构造方法私有化private void MyBatisUtil() {}//方法一public static SqlSession getSqlSession(){try {SqlSessionFactoryBuilder sql…

Xilinx DDR4 MIG 的调试

平台&#xff1a;Vivado2021.1 芯片&#xff1a;xcku115-flva1517-2-i (active) 语言&#xff1a;VerilogHDL 参考文件&#xff1a;pg150.下载地址 pg150-ultrascale-memory-ip.pdf • 查看器 • AMD 自适应计算文档门户 (xilinx.com) DDR4介绍 DDR4&#xff0c;即第四代…

隐藏Zotero批注图标(便利贴)

使用Zotero看文献做批注的时候&#xff0c;会出现图上类似于便利贴的图标&#xff0c;容易遮挡字体。 在"编辑"中选择"首选项"&#xff0c;"翻译"中去掉勾选"自动翻译批注"。 就好了

python二次开发Solidworks:扫描

在新零件文档中创建一个圆形草图和两条线段草图&#xff0c;并将它们插入到模型中。接着&#xff0c;选中圆形草图作为扫描轮廓&#xff0c;并选中两条线段草图并将它们分组为一个对象。最后&#xff0c;使用特征管理器的InsertProtrusionSwept4方法创建扫描特征。 import win…

gson如何序列化子类

需求 目前有一个需求&#xff0c;不同对象有一些公共属性&#xff0c;分别也有一些不同的属性。对方传过来的json字符串中&#xff0c;把这些对象组成了一个数组返回过来的。这样该如何反序列化呢&#xff1f; 举例 定义Person类、Student类、Worker类&#xff1b; Data To…

【MATLAB第80期】基于MATLAB的结构核岭回归SKRR多输入单输出回归预测及分类预测模型

【MATLAB第80期】基于MATLAB的结构核岭回归SKRR多输入单输出回归预测及分类预测模型 SKRR这是Gustau Camps-Valls等人在“用深度结构核回归检索物理参数”中提出的结构核岭回归&#xff08;SKRR&#xff09;方法。 参考文献&#xff1a; Camps-Valls,Retrieval of Physical Pa…

期中考核复现(web)

1z_upload 进到这个页面里面还是挺懵的&#xff0c;什么也不知道 点添加书籍之后发现变成了sql 师兄给了源码&#xff0c;看了之后找到了他的限制函数 但是肯定要先登录才可以 登录还是用bp爆破 最后得到账号密码是admin/admin12345 进来之后就可以看到那个文件上传的地方 上…

RabbitMQ入门到实战教程,MQ消息中间件,消息队列实战

消息队列是目前最常见的微服务中间件之一&#xff0c;而RabbitMq在全球范围内的使用率也是名列前茅。它以稳定性强、并发高、低时延的特点深受广大企业开发者的喜爱。然而市面上一致缺乏一个专门的RabbitMQ课程&#xff0c;所以这套课程应运而生。 微服务一旦拆分&#xff0c;必…

【Bug】【内存相关】偶然发现一个内存溢出Bug复盘

一、问题 跑自动化用例的时候&#xff0c;uat-sg环境&#xff0c;发现SGW经常会返回 502 Bad Gateway响应 二、原因 经过SRE和BE Dev共同排查&#xff0c;502 是从ALB-- > 后端服务 后端服务无法响应导致&#xff0c;ALB会直接给客户端返回502。 服务端&#xff1a;由于c…

C# Winform编程(6)高级控件

C# Winform编程&#xff08;6&#xff09;高级控件 RadioButton&#xff08;单选框&#xff09;PictureBox&#xff08;图像框&#xff09;TabControl&#xff08;选项卡&#xff09;ProgressBar(进度条)TrackBar(滑动条)ImageList&#xff08;图像列表控件&#xff09;ToolBar…

29栈与队列——优先队列

目录 LeetCode之路——347. 前 K 个高频元素 分析 优先队列 简单示例 运行结果 源码简析 LeetCode之路——347. 前 K 个高频元素 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: num…

一剑破万法:noexcept与C++异常导致的coredump

作为C/C程序员&#xff0c;最不想见到的就是coredump。导致coredump的原因有很多&#xff0c;今天我来谈一下其中一种十分常见的原因&#xff0c;那就是由于C异常没有被catch导致的coredump。 从一篇知乎文章讲起 先看一位知友的文章&#xff1a; C11 std::thread异常coredu…

[ROS2系列] ORBBEC(奥比中光)AstraPro相机在ROS2进行rtabmap 3D建图

目录 背景&#xff1a; 一、驱动AstraPro摄像头 二、安装rtabmap error1&#xff1a;缺包 三、尝试 四、参数讲解 五、运行 error2: Did not receive data since 5 seconds! 六、效果​编辑 error4: 背景&#xff1a; 1、设备&#xff1a;pc&#xff1b;jeston agx …

使用kyuubi查询出现集群单节点cpu飙升问题

问题现象&#xff1a; 单节点cpu飙升 初步定位原因是: 业务人在查询操作时&#xff0c;表被锁表&#xff0c;查询不出结果&#xff0c;就反复执行&#xff0c;导致堆积&#xff0c;并kyuubi又是高并发操作。 &#xff08;当一些操作在共享锁阻塞期间积压时&#xff0c;这些操…

Leetcode 349 两个数组的交集 (*哈希数组,*HashSet,*HashMap)

Leetcode 349 两个数组的交集 &#xff08;*哈希数组&#xff0c;*HashSet&#xff0c;*HashMap&#xff09; 解法1 [用数组构建hashmap] &#x1f60b;HashSet and .HashMap1.HashSet2.HashMap 解法2 [使用HashSet]⭐️ 解法1 [用数组构建hashmap] &#x1f60b; 自己的笨比方…

【ES实战】ES主副分片数据不一致分析

ES主副分片数据不一致分析 文章目录 ES主副分片数据不一致分析问题描述问题重现问题分析修复方案 问题描述 在请求索引中的某一条数据时&#xff0c;时而查询有结果&#xff0c;时而无结果。两种情况交替出现。 问题重现 通过对问题数据的点查&#xff0c;确实重现了该现象 …

CSDN 操作

CSDN的目录界面&#xff0c;会随着浏览器界面的大小而变化&#xff0c;分布在左边位置或者右边位置。 目录界面在右&#xff1a; 目录界面在左&#xff1a; 按键Ctrl&#xff0c; 滑动鼠标滚轮&#xff0c;就可以放大或者缩小界面。

Intelijj使用Gitee团队开发

初始化项目到Gitee服务器 成功标识&#xff1a; 添加团队成员 点击管理——仓库成员设置——开发者 2.添加仓库成员 &#xff08;最多不超过5人&#xff09; 3.通过链接或者二维码邀请新成员&#xff0c;或者可以自己手动添加新成员并提交 多人项目仓库创建完成 通…