我们平时做项目主要面向企业客户的业务系统,企业的需求往往是多样化且复杂多变的,对接不同企业时会有不同的定制化的业务模型和流程。我们在业务系统中使用表达式引擎,集中配置管理业务规则,并实现实时决策和计算,可以提高系统的灵活性和响应能力,从而更好地满足业务的需求。
规则引擎的一个好处是可以使业务规则和业务代码分离,从而降低维护难度,同时它还可以满足业务人员通过界面编写规则,这样就可以在没有开发人员参与的情况下建立规则,这种说法听起来似乎很有道理,但在实践中却很少行得通。首先,规则引擎有一定的学习成本,即使开发人员使用也需要进行专门的学习,更何况没有任何编程背景的业务人员,其次,其实现的复杂度也高,如果业务规则复杂,规则制定者对规则引擎内部隐藏的程序流程不了解,很可能会得到意想不到的结果,最后,有些规则引擎还存在性能瓶颈。
本文对一些常用的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; } }