Java表达式和规则引擎的比较与考量

简介: Java表达式和规则引擎的比较与考量

  我们平时做项目主要面向企业客户的业务系统,企业的需求往往是多样化且复杂多变的,对接不同企业时会有不同的定制化的业务模型和流程。我们在业务系统中使用表达式引擎,集中配置管理业务规则,并实现实时决策和计算,可以提高系统的灵活性和响应能力,从而更好地满足业务的需求。

    规则引擎的一个好处是可以使业务规则和业务代码分离,从而降低维护难度,同时它还可以满足业务人员通过界面编写规则,这样就可以在没有开发人员参与的情况下建立规则,这种说法听起来似乎很有道理,但在实践中却很少行得通。首先,规则引擎有一定的学习成本,即使开发人员使用也需要进行专门的学习,更何况没有任何编程背景的业务人员,其次,其实现的复杂度也高,如果业务规则复杂,规则制定者对规则引擎内部隐藏的程序流程不了解,很可能会得到意想不到的结果,最后,有些规则引擎还存在性能瓶颈。

   本文对一些常用的java表达式和规则引擎进行比较,以便后续进行选择使用。

   1、Aviator表达式引擎

        Aviator是一个独立的表达式解析引擎,旨在执行数学和逻辑表达式。它提供了简单易用的语法和API,使得表达式解析和计算变得简单而直观。通过使用Aviator,我们可以高效地执行各种数学和逻辑运算,从而简化我们的代码逻辑。

      比如在flowable流程可以用下面进行流程条件的判断

/**
     * 校验el表达式
     *
     * @param map
     * @param expression
     * @return
     */
    public static boolean expressionResult(Map<String, Object> map, String expression) {
        Expression exp = AviatorEvaluator.compile(expression);
        final Object execute = exp.execute(map);
        return Boolean.parseBoolean(String.valueOf(execute));
    }

Aviator的特点

     高性能:Aviator被设计为高性能的表达式解析引擎。它使用解释器和JIT(Just-In-Time)编译器的混合模式,将表达式转换为优化的字节码,从而实现快速的表达式求值和计算。

灵活的表达式语法:Aviator的语法类似于Java语言,易于理解和编写表达式。它支持各种数学和逻辑运算符,以及丰富的内置函数,使得我们可以编写更灵活和功能强大的表达式。

自定义函数支持:Aviator允许我们定义自己的函数,并将其注册到引擎中供表达式使用。这样,我们可以根据实际需求扩展表达式的功能,满足特定的业务逻辑需求。

安全性和可扩展性:Aviator提供了安全的表达式执行环境,可以控制表达式对环境的访问权限。同时,它还支持自定义的上下文对象,可以在表达式求值过程中传递额外的上下文信息。

Aviator表达式引擎适用于多种应用场景:

规则引擎:通过Aviator,我们可以实现灵活的规则匹配和动态规则更新,例如金融领域的风控系统。

计算引擎:Aviator可以作为高性能的计算引擎,支持数学计算、数据分析和科学计算等任务。

动态

2、Fel轻量高效的表达式计算引擎

       Fel是开放的,引擎执行中的多个模块都可以扩展或替换。Fel的执行主要是通过函数实现,运算符(+、-等都是Fel函数),所有这些函数都是可以替换的,扩展函数也非常简单。

Fel有双引擎,同时支持解释执行和编译执行。可以根据性能要求选择执行方式。编译执行就是将表达式编译成字节码(生成java代码和编译模块都是可以扩展和替换的)

     通常情况下,Fel-0.7每秒可以执行千万次表达式(不包含编译时间)。速度是Jexl-2.0的20倍以上。目前还没有发现开源的表达式引擎比Fel快。

    比如在flowable的部分代码如下:

/**
     * 校验el表达示例
     *
     * @param map
     * @param expression
     * @return
     */
    public static Object result(Map<String, Object> map, String expression) {
        FelEngine fel = new FelEngineImpl();
        FelContext ctx = fel.getContext();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            ctx.set(entry.getKey(), entry.getValue());
        }
        Object result = fel.eval(expression);
        return result;
    }

     目前我的项目里暂时使用了Fel。

 3、Easy Rules

      Easy Rules 是一款 Java 规则引擎,是轻量级的规则引擎API,它提供Rule抽象来创建带有条件和动作的规则,以及RulesEngine通过一组规则运行以测试条件和执行动作的API。

      Easy Rules 提供了规则抽象来创建带有条件和操作的规则,以及运行一组规则来评估条件和执行操作的RulesEngine API。

      但这个项目的问题是好久没维护了。

      下面是注解方式:

@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 weatherRule = new RuleBuilder()
    .name("weather rule")
    .description("if it rains then take an umbrella")
    .when(facts -> facts.get("rain").equals(true))
    .then(facts -> System.out.println("It rains, take an umbrella!"))
    .build();

4、Drools

      Drools 是用Java语言编写的开放源码规则引擎,基于Apache协议,基于RETE算法,于2005年被JBoss收购。

    Drools是一个绝对重量级的规则引擎,很多像金融行业、电信行业的大公司都在使用它作为规则引擎。

     Drools通过 事实、规则和模式相互组合来完成工作,drools在开源规则引擎中使用率最广,但是在国内企业使用偏少,保险、支付行业使用稍多。

    这个是flowable系统内部本身采用的规则引擎。

    下面是一个flowable商用规则任务的实现

package org.flowable.engine.impl.bpmn.behavior;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.drools.KnowledgeBase;
import org.drools.runtime.StatefulKnowledgeSession;
import org.flowable.engine.delegate.BusinessRuleTaskDelegate;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.common.api.delegate.Expression;
import org.flowable.engine.impl.rules.RulesAgendaFilter;
import org.flowable.engine.impl.rules.RulesHelper;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.repository.ProcessDefinition;
/**
 * Activity implementation of the BPMN 2.0 business rule task.
 * 
 * @author Tijs Rademakers
 * @author Joram Barrez
 */
public class BusinessRuleTaskActivityBehavior extends TaskActivityBehavior implements BusinessRuleTaskDelegate {
    private static final long serialVersionUID = 1L;
    protected Set<Expression> variablesInputExpressions = new HashSet<>();
    protected Set<Expression> rulesExpressions = new HashSet<>();
    protected boolean exclude;
    protected String resultVariable;
    public BusinessRuleTaskActivityBehavior() {
    }
    @Override
    public void execute(DelegateExecution execution) {
        ProcessDefinition processDefinition = ProcessDefinitionUtil.getProcessDefinition(execution.getProcessDefinitionId());
        String deploymentId = processDefinition.getDeploymentId();
        KnowledgeBase knowledgeBase = RulesHelper.findKnowledgeBaseByDeploymentId(deploymentId);
        StatefulKnowledgeSession ksession = knowledgeBase.newStatefulKnowledgeSession();
        if (variablesInputExpressions != null) {
            Iterator<Expression> itVariable = variablesInputExpressions.iterator();
            while (itVariable.hasNext()) {
                Expression variable = itVariable.next();
                ksession.insert(variable.getValue(execution));
            }
        }
        if (!rulesExpressions.isEmpty()) {
            RulesAgendaFilter filter = new RulesAgendaFilter();
            Iterator<Expression> itRuleNames = rulesExpressions.iterator();
            while (itRuleNames.hasNext()) {
                Expression ruleName = itRuleNames.next();
                filter.addSuffic(ruleName.getValue(execution).toString());
            }
            filter.setAccept(!exclude);
            ksession.fireAllRules(filter);
        } else {
            ksession.fireAllRules();
        }
        Collection<Object> ruleOutputObjects = ksession.getObjects();
        if (ruleOutputObjects != null && !ruleOutputObjects.isEmpty()) {
            Collection<Object> outputVariables = new ArrayList<>();
            outputVariables.addAll(ruleOutputObjects);
            execution.setVariable(resultVariable, outputVariables);
        }
        ksession.dispose();
        leave(execution);
    }
    @Override
    public void addRuleVariableInputIdExpression(Expression inputId) {
        this.variablesInputExpressions.add(inputId);
    }
    @Override
    public void addRuleIdExpression(Expression inputId) {
        this.rulesExpressions.add(inputId);
    }
    @Override
    public void setExclude(boolean exclude) {
        this.exclude = exclude;
    }
    @Override
    public void setResultVariable(String resultVariableName) {
        this.resultVariable = resultVariableName;
    }
}


相关文章
|
10月前
|
安全 Java API
Java中的Lambda表达式:简洁与功能的结合
Java中的Lambda表达式:简洁与功能的结合
570 211
|
10月前
|
安全 Java
Java中的Switch表达式:更简洁的多路分支
Java中的Switch表达式:更简洁的多路分支
751 211
|
10月前
|
Java 编译器
Java 17 Switch表达式:更简洁、更强大的流程控制
Java 17 Switch表达式:更简洁、更强大的流程控制
1202 111
|
10月前
|
Java 编译器 API
Java Lambda表达式与函数式编程入门
Lambda表达式是Java 8引入的重要特性,简化了函数式编程的实现方式。它通过简洁的语法替代传统的匿名内部类,使代码更清晰、易读。本文深入讲解Lambda表达式的基本语法、函数式接口、方法引用等核心概念,并结合集合操作、线程处理、事件回调等实战案例,帮助开发者掌握现代Java编程技巧。同时,还解析了面试中高频出现的相关问题,助你深入理解其原理与应用场景。
|
11月前
|
自然语言处理 Java Apache
在Java中将String字符串转换为算术表达式并计算
具体的实现逻辑需要填写在 `Tokenizer`和 `ExpressionParser`类中,这里只提供了大概的框架。在实际实现时 `Tokenizer`应该提供分词逻辑,把输入的字符串转换成Token序列。而 `ExpressionParser`应当通过递归下降的方式依次解析
506 14
|
12月前
|
SQL JSON 安全
Java 8 + 中 Lambda 表达式与 Stream API 的应用解析
摘要:本文介绍了Java 8+核心新特性,包括Lambda表达式与Stream API的集合操作(如过滤统计)、函数式接口的自定义实现、Optional类的空值安全处理、接口默认方法与静态方法的扩展能力,以及Java 9模块化系统的组件管理。每个特性均配有典型应用场景和代码示例,如使用Stream统计字符串长度、Optional处理Map取值、模块化项目的依赖声明等,帮助开发者掌握现代Java的高效编程范式。(150字)
271 1
|
11月前
|
设计模式 数据采集 Java
Java正则表达式的基础知识,进阶至熟练掌握。
通过大量的练习来熟悉它们的识别模式、如何设计模式来解决实际问题,才能够逐步达到熟练掌握。更多的是通过实践、编写代码和解决真实问题来完善技能。在这方面,没有快速的捷径,唯有刻意练习和长时间的代码实践。
222 0
|
缓存 监控 Java
深入解析java正则表达式
本文深入解析Java正则表达式的应用,从基础概念到实际开发技巧全面展开。正则表达式是一种强大的文本处理工具,广泛应用于格式验证、搜索替换等场景。Java通过`Pattern`和`Matcher`类支持正则表达式,`Pattern.compile()`方法将正则字符串编译为高效模式对象。文章详细介绍了核心类的功能、常用正则语法及实际案例(如邮箱和电话号码验证)。掌握这些内容,可显著提升文本处理能力,满足多种开发需求。
414 1
|
SQL IDE 算法
《从头开始学java,一天一个知识点》之:运算符与表达式:算术、比较和逻辑运算
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 这个系列为你打造Java「速效救心丸」,每天1分钟,地铁通勤、午休间隙即可完成学习。直击高频考点和实际开发中的「坑位」,拒绝冗长概念,每篇都有可运行的代码示例。明日预告:《控制流程:if-else条件语句实战》。
366 6