表白式引擎选型调研剖析-Java (表白模式来了)
一、简介
咱们名目组关键担任面向企业客户的业务系统, 企业的需求往往是多样化且复杂的,对接不同企业时会有不同的定制化的业务模型和流程 。咱们在业务系统中 经常使用表白式引擎,集中性能治理业务规定,并实事实时决策和计算,可以提高系统的灵敏性和照应才干 ,从而更好地满足业务的需求。
举个繁难的例子,假定咱们有一个业务场景,在返利系统中,当推行员满足必定的鼓励条件时,就会给其对应的鼓励金额。例如某个产品的详细鼓励规定如下:
这个规定看起来很好成功,只需在代码里写几个ifelse分支就可以了。然而假设返利系统对接了多家供应商,且每家提供的产品的鼓励规定都不同呢?再经过硬编码的形式写ifelse仿佛就不太好了,每次参与修正删除规定都须要系统发版上线。
引入规定引擎仿佛就能处置这个疑问,规定引擎的一个好处就是可以使业务规定和业务代码分别,从而降落保养难度,同时它还可以满足业务人员经过编写DSL或经过界面指定规定的诉求,这样就可以在没有开发人员介入的状况下建设规定了,这种说法听起来仿佛很有情理,但在通常中却很少行得通。首先,规定引擎有必定的学习老本,即使开发人员经常使用也须要启动专门的学习,更何况没有任何编程背景的业务人员,其次,其成功的复杂度也高,假设业务规定复杂,规定制订者对规定引擎外部暗藏的程序流程不了解,很或许会失掉意想不到的结果,最后,有些规定引擎还存在性能瓶颈。假设对规定引擎和表白式引擎都不相熟,抽离的业务规定又须要由开发人员来制订,那么 相比之下表白式引擎就要容易上手得多,其语法更凑近,而且有些表白式引擎还会将表白式编译成字节码,在口头速度和资源应用方面或许就更有长处。 所以,关于此类业务场景,经常使用表白式引擎仿佛愈加适宜一些。
本文关键对Java表白式引擎启动概要性引见和剖析,并提供必定倡导,为团队研发环节中对表白式引擎的技术选型提供输入。
二、技术栈简介
本文将针对Aviator、MVEL、OGNL、SpEL、QLExpress、JEXL、JUEL几种经常出现表白式引擎启动选型调研。先繁难引见一下这几种表白式引擎。
2.1Aviator
Aviator是一门高性能、轻量级寄宿于JVM之上的脚本言语。Aviator可将表白式编译成字节码。它原来的定位不时只是一个表白式引擎,不支持if/else条件语句,也不支持for/while循环语句等,随着5.0的发布变身为一个通用脚本言语,支持了这些言语个性。
文档:
2.2MVEL(MVFLEXExpressionLanguage)
MVEL是一种混合的灵活/静态类型的、可嵌入Java平台的表白式言语,MVEL被泛滥Java名目经常使用。MVEL在很大水平上遭到Java语法的启示,但也有一些实质区别,目的是使其作为一种表白式言语愈加高效,例如直接支持汇合、数组和字符串婚配的操作符,以及正则表白式。最早版本发布于2007年。
文档:
2.3OGNL(Object-GraphNavigationLanguage)
OGNL是Object-GraphNavigationLanguage(对象图导航言语)的缩写;它是一种表白式言语,用于失掉和设置Java对象的属性,以及其余额外性能,如列表投影和决定以及lambda表白式。于2005年发布2.1.4版。
文档:
2.4SpEL(SpringExpressionLanguage)
SpEL是一种性能弱小的表白式言语,支持在运转时查问和操作对象图。该言语的语法与UnifiedEL相似,但提供了更多的性能,其中最关键的是方法调用和基本的字符串模板性能。
文档:
2.5QLExpress
由阿里的电商业务规定、表白式(布尔组合)、不凡数学公式计算(高精度)、语法剖析、脚本二次定制等强需求而设计的一门灵活脚本引擎解析工具,于2012年开源。
文档:
2.6JEXL(JavaExpressionLanguage)
JEXL旨在促成在Java编写的运行程序和框架中成功灵活脚本色能。JEXL基于对JSTL表白式言语的一些裁减成功了一种表白式言语,支持shell脚本或ECMA中的大局部设想。1.0版发布于2005年。
文档:
2.7JUEL(JavaUnifiedExpressionLanguage)
JUEL是一致表白式言语(EL)的成功,该言语是JSP2.1规范(JSR-245)的一局部,已在JEE5中引入。此外,JUEL2.2成功了JSP2.2保养版本规范,齐全合乎JEE6规范。于2006年发布2.1.0版本,2.2.7发布于2014年。
文档:
Janino是一个超小、超快的Java编译器,也可以用作表白式引擎,它的性能十分杰出,依据官方引见,ApacheSpark、ApacheFlink、Groovy等优秀的开源名目都在用Janino。
文档:
由于Janino实践是一个Java编译器,通常上其性能应该更凑近于直接口头Java代码,其次作为表白式引擎经常使用起来比拟复杂。因此,上方的对比中,Janino不介入比拟,可以将其作为一个参照。
2.9其余
如下一些表白式引擎虽然也经常出现于各技术博客,但由于常年没有降级保养,因此没有归入此次选型比拟
Fel是轻量级的高效的表白式计算引擎。Fel源自于企业名目,设计指标是为了满足不时变动的性能需求和性能需求。名目托管于Code,上次降级是2012年,曾经十几年没有降级了,所以没有归入此次选型。
ik-expression
IKExpression是一个开源的(OpenSource),可裁减的(Extensible),基于java言语开发的一个超轻量级(Superlightweight)的公式化言语解析口头工具包。2009年2月发布第一个版本,2009年10月发布最后一个版本后再没有新版本发布,所以没有归入此次选型。
JSEL是一个兼容Java运算规定的繁难的表白式解释引擎,你可以经过Map接口,或许JavaBean给出一个变量汇合,能后经过表白式从这个汇合中抽取变量,再经过表白式逻辑生成你须要的数据。2009年发布第一个版本,2011年发布最后一个版本后未再降级,所以没有归入此次选型。
此外规定引擎如Drools,urule,easy-rules不介入此次选型比拟。相对比拟成熟完善的脚本言语如Groovy也不介入选型比拟。这篇文章关键针对相对轻量繁难的表白式引擎启动选型。
三、技术栈选型评价
决定表白式引擎,咱们宿愿其社区支持状况良好、成功复杂度适中、口头速度快、安保并且繁难易学。所以,接上去将 从社区支持状况、引入的大小和依赖、性能、安保性、经常使用案例和语法几个方面对几种表白式引擎启动比拟评价。
3.1社区支持状况
社区支持状况可以辅佐评价名目的肥壮度,有疑问是不是能及时处置,名目是不是能继续演进等等 ,上方列出了GitHubstar,watch,fork,lastcommit等数据,可以作为参考,由于数据随着期间推移会发生变动,以下仅针对2023.10.29的数据启动剖析。
由于Spring名目被宽泛经常使用,而SpEl又是Spring的一个子名目,所以从各项数据来看SpEl的社区支持状况是最好的。上方先扫除SpEl剖析其余几个表白式引擎。
QLExpress,Aviator和MVEL在国际经常使用比拟多,这或许是他们star,watch,fork数较高的要素。说明这几个名目受欢迎度,受认可度,影响力应该较高。
从issues,pullrequests数来剖析,可以看到MVEL,Aviator和QLExpress高于其余脚本引擎,说明他们的用户需求和反应较多,也或许象征着名目面临较多疑问和应战。
MVEL,JEXL,OGNL均有较多奉献者介入。他们的社区单干、名目可继续性方面应该都比拟不错。
综合以上剖析,除SpEl外,QLExpress,Aviator和MVEL的社区支持状况都相对较好。
3.2引入大小和依赖
代码大小和依赖可以辅佐评价代码的复杂性,上方列出了各个Github仓库的代码大小,可以作为一个参考(实践并不齐全准确反映其成功的复杂性)。
以下是2023.10.29的数据
JUEL,QLExpress代码大小最小,都在600多KB;其次是OGNL1MB多一点;Aviator,MVEL,JEXL大小都在2MB左右;SpEl由于在spring-framework仓库中,上表中统计的是spring-framework的总量,单纯看SpEl的模块spring-expression的话,大小是1.3MB左右。然而其还依赖了spring-core和spring-jcl,再含这两个的话,大小7.4MB左右。
咱们再联合各个名目的依赖来剖析一下。
+-org.mvel:mvel2:jar:2.5.0.Final:compile+-com.googlecode.aviator:aviator:jar:5.3.3:compile+-com.alibaba:QLExpress:jar:3.3.1:compile|+-commons-beanutils:commons-beanutils:jar:1.8.2:compile||-(commons-logging:commons-logging:jar:1.1.1:compile-omittedforconflictwith1.2)|-commons-lang:commons-lang:jar:2.4:compile+-org.codehaus.janino:janino:jar:3.1.10:compile|-org.codehaus.janino:commons-compiler:jar:3.1.10:compile+-ognl:ognl:jar:3.4.2:compile|-org.javassist:javassist:jar:3.29.2-GA:compile+-org.apache.commons:commons-jexl3:jar:3.3:compile|-commons-logging:commons-logging:jar:1.2:compile+-org.springframework:spring-expression:jar:5.3.29:compile|-org.springframework:spring-core:jar:5.3.29:compile|-org.springframework:spring-jcl:jar:5.3.29:compile+-de.odysseus.juel:juel-api:jar:2.2.7:compile+-de.odysseus.juel:juel-impl:jar:2.2.7:compile+-de.odysseus.juel:juel-spi:jar:2.2.7:compile除了SpEl外,QLExpress,OGNL,JEXL也都有其余依赖。
假设思考commons-beanutils,commons-lang,commons-logging三个依赖,QLExpress引入的大小在10MB左右。
假设思考javassist依赖,OGNL引入的大小是4MB多。
假设思考commons-logging依赖,JEXL引入的大小是2.5MB左右。
综合来看,JUEL,Aviator,MVEL,JEXL在引入大小和依赖方面要好于其余。
3.3性能
较好的性能象征着系统能够极速地响运行户的恳求,缩小期待期间,优化体验。
性能方面关键经过JMH在字面量表白式、含有变量的表白式以及含有方法调用的表白式等经常使用场景对几个表白式引擎启动测试。
JMH(JavaMicrobenchmarkHarness),是用于代码微基准测试的工具套件,关键是基于方法层面的基准测试,精度可以到达纳秒级。该工具是由Oracle外部成功JIT的大牛们编写的,他们应该比任何人都了解JIT以及JVM关于基准测试的影响。
由于不同表白式引擎语法或个性稍有差异,上方测试中关于差异项会启动说明。
性能测试代码地址:GitHub-
3.3.1字面量表白式
:1000+100.0*99-(600-3*15)/(((68-9)-3)*2-100)+10000%7*71
:6.7-100>39.6?5==5?4+5:6-1:!(100%3-39.0<27)?8*2-199:100%3
说明:
由于QlExpress口头第2个表白式时报错,须要参与圆括号,实践口头的是6.7-100>39.6?(5==5?4+5:6-1):(!(100%3-39.0<27)?8*2-199:100%3)
结果剖析:
可以清楚看到JEXL,JUEL,QlExpress这三个表白式引擎性能清楚不如其余引擎。
SpEl在口头第1个算数操作时体现杰出,然而在口头第2个嵌套三元操作时清楚不如Aviator,MVEL,OGNL引擎。
此轮测试中Aviator,OGNL,MVEL体现杰出。Aviator,OGNL口头两个表白式体现都比拟杰出,其中Aviator略好于OGNL。MVEL在口头第1个算数操作时体现最杰出,然而在口头第2个嵌套三元操作时慢于Aviator,OGNL引擎。
3.3.2含有变量的表白式
:pi*d+b-(1000-d*b/pi)/(pi+99-i*d)-i*pi*d/b
:piDecimal*dDecimal+bDecimal-(1000-dDecimal*bDecimal/piDecimal)/(piDecimal+99-iDecimal*dDecimal)-iDecimal*piDecimal*dDecimal/bDecimal
:i*pi+(d*b-199)/(1-d*pi)-(2+100-i/pi)%99==i*pi+(d*b-199)/(1-d*pi)-(2+100-i/pi)%99
:(clientVersion=='1.9.0'||clientVersion=='1.9.1'||clientVersion=='1.9.2')&&deviceType=='Xiaomi'&&weight>=4&&osVersion=='9.0'&&osType=='Android'&&clientIp!=null&&requestTime<=now&&customer.grade>1&&customer.age>18
说明:
结果剖析:
第1个基本类型包装类的算术计算SpEl最优。其次是Aviator,MVEL,OGNL。而JEXL,JUEL,QlExpress则不如其余引擎。
第2个BigDecimal类型的算术计算。由于底层成功不同,分为两组。第1组MVEL、Aviator和JEXL,Aviator优于MVEL优于JEXL。第2组JUEL,QlExpress,OGNL和SpEl,性能由优到差依次是OGNL,SpEl,JUEL,QlExpress。并且第1组由于精度更高,性能清楚都差于第2组。
第3个含有基本类型包装类算数计算的布尔表白式。SpEl最优,Aviator次之,接上去依次是OGNL,MVEL,JUEL,JEXL,QlExpress。
第4个含有字符串比拟的布尔表白式。Aviator,MVEL,JEXL,OGNL性能优于JUEL,QlExpress,SpEl。
3.3.3含有方法调用的表白式
:newjava.util.Date
:s.substring(b.d)
:s.substring(b.d).substring(a,b.c.e)
说明:
结果剖析:
此轮测试中SpEl的体现最优,甚至比Janino还要快。MVEL,Aviator次之,在口头结构方法时MVEL要好于Aviator。JEXL体现也比拟杰出。QlExpress,JUEL,OGNL这三个表白式引擎则不如其余引擎。
3.3.4总结
综合以上测试结果,Aviator,SpEl,MVEL,OGNL性能体现相对较好。
Aviator性能相对较好,体现平衡,但其语法相较其余引擎跟Java的差异略大。
SpEl除了在一般场景下性能较差,大局部场景体现十分杰出,尤其是在字面量和含有变量的算数计算及方法调用场景下。
MVEL性能体现相对平衡,含有变量的算术计算略差于Aviator,其在字面量算术计算,方法调用场景下体现都十分杰出。
OGNL性能体现也相对平衡,但方法调用场景下体现不佳。
3.4安保
引入表白式引擎,应该注重系统的安保性和牢靠性,比如要防止在无法信环境中被注入恶意脚本,越权口头某些系统命令或使运行中止服务等。 安保性方面关键经过破绽披露、安保指南和性能比拟几种表白式引擎。
3.4.1破绽
首先在经过关键字搜查的形式粗略了解一下不同表白式引擎被地下的破绽。这种形式或许不是十分的准确,由于不同表白式引擎的经常使用场景、经常使用形式、关注度的不同或许造成被地下的破绽存在差异。比如咱们所相熟的OGNL、SpEl的关键字出如今破绽中的频率清楚高于其余表白式引擎。OGNL在和Struts中被经常使用,SpEl则在Spring中被宽泛经常使用,这两个表白式引擎会被大局部名目直接经常使用,直接将用户输入作为表白式的一局部口头,很容易造成发生破绽。
咱们 可以从这些发布的破绽中了解不同表白式引擎或许存在的安保隐患及其修复状况,在经常使用环节中尽或许防止发生相似疑问。
此外,不介绍将表白式口头直接开明到无法信的环境,假设确实须要,应该详细了解决定的表白式引擎,能否提供了必要的设置选项可以防止某些安保隐患。
3.4.2安保设置
Aviator,QLExpress,JEXL均从不同水平提供了一些安保选项设置。
//在new语句和静态方法调用中准许经常使用的类白名单自动null示意有限度AviatorEvaluator.setOption(Options.ALLOWED_CLASS_SET,Sets.newHashSet(List.class));//在new语句和静态方法调用中准许经常使用的类白名单蕴含子类自动null示意有限度AviatorEvaluator.setOption(Options.ASSIGNABLE_ALLOWED_CLASS_SET,Sets.newHashSet(List.class));//循环最大次数自动0示意有限度AviatorEvaluator.setOption(Options.MAX_LOOP_COUNT,10000);//封锁某些个性AviatorEvaluator.getInstance.disableFeature(Feature.Module);AviatorEvaluator.getInstance.disableFeature(Feature.NewInstance);//只开启须要的个性AviatorEvaluator.setOption(Options.FEATURE_SET,Feature.asSet(Feature.If));QLExpressRunStrategy.setSandBoxMode(true);在沙箱形式中,无法以:
◦importJava类
◦显式援用Java类,比如Stringa='mmm'
◦取Java类中的字段:a=newInteger(11);a.value
◦调用Java类中的方法:Math.abs(12)
可以:
◦经常使用QLExpress的自定义操作符/宏/函数,以此成功与运行的受控交互
◦经常使用.操作符失掉Map的key对应的value,比如a在运行传入的表白式中是一个Map,那么可以经过a.b失掉
◦一切不触及运行Java类的操作
//设置编译期白名单QLExpressRunStrategy.setCompileWhiteCheckerList(Arrays.asList(//准确设置CheckerFactory.must(Date.class),//子类设置CheckerFactory.assignable(List.class)));//设置运转时白名单//必定将该选项设置为trueQLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);//有白名单设置时,则黑名单失效QLExpressRunStrategy.addSecureMethod(RiskBean.class,"secureMethod");//必定将该选项设置为trueQLExpressRunStrategy.setForbidInvokeSecurityRiskMethods(true);//这里不辨别静态方法与成员方法,写法分歧//不支稳健载,riskMethod的一切重载方法都会被制止QLExpressRunStrategy.addSecurityRiskMethod(RiskBean.class,"riskMethod");QLExpess目前自动参与的黑名单有:
◦java.lang.System.exit
◦java.lang.Runtime.exec
◦java.lang.ProcessBuilder.start
◦java.lang.reflect.Method.invoke
◦java.lang.reflect.Class.forName
◦java.lang.reflect.ClassLoader.loadClass
◦java.lang.reflect.ClassLoader.findClass
//可经过timeoutMillis参数设置脚本的运转超时期间:1000msObjectr=runner.execute(express,context,null,true,false,1000);//经常使用中应该经过JexlSandbox的重载结构方法启动性能newJexlBuilder.sandbox(newJexlSandbox).create;newJexlBuilder.permissions(JexlPermissions.RESTRICTED.compose("com.jd.*")).create;//封锁循环、new实例,import等个性newJexlBuilder.features(newJexlFeatures.loops(false).newInstance(false).importPragma(false)).create;3.5经常使用案例
从业界经常使用状况可以了解不同表白式引擎的可行性、生态和整合性,以及最佳通常,进而自创。从下表可以看到Aviator,MVEL,QLExpress在国际业务线均有经常使用案例,有些企业也有文章输入,咱们可以自创经常使用。
3.6语法
易于了解和经常使用的语法可以提高开发效率,并降落学习老本。接上去从类型、操作符、控制语句、汇合、方法定义几方面比拟一下不同表白式引擎的语法设计。
类型方面,Aviator设计了特有的类型,经常使用时须要留意其类型转换的优先级long->bigint->decimal->double。Aviator、MVEL、OGNL、JEXL都支持BigInteger、BigDecimal字面量,这象征着启动准确计算时可以经常使用字面量,将更繁难,如10.24B就示意一个BigDecimal字面量(Aviator中BigDecimal字面量后缀是M)。此外Aviator、QLExpress还支持高精度计算的设置项。
操作符方面,QLExpress支持交流、自定义操作符及参与操作符别名,这或许有助于简化复杂表白式或使表白式愈加直观,不过参与预置函数应该可以到达差不多的成果。Aviator也支持自定义局部操作符,不过支持数量相当有限。Aviator、SpEl、JEXL支持正则婚配操作符。
控制语句方面,除OGNL、SpEl、JUEL不支持控制语句外,其余都支持,不过须要留意Aviator的elseif语法有些不凡写作elsif,foreach语句跟Java也有所不同。
汇合方面,除JUEL外其余都提供了快捷定义的形式,只不过语法不同。
函数定义方面,SpEl、JUEL均不支持,OGNL支持伪lambda定义,其余都支持定义函数。QLExpress不支持定义lambda。
综合来看,和Java语法都或多或少存在一些差异。 Aviator设计了自己特有的一些语法,经常使用的话须要相熟一下。QLExpress支持自定义操作符,可以使表白式看起来更直观。MVEL、JEXL的语法或许更凑近Java,让人更容易接受一些。OGNL、SpEl、JUEL的语法更繁难一些,不支持控制语句和函数定义,当然也可以经过预置一些函数变通处置一些较复杂的疑问。
四、选型倡导
社区方面,SpEl无疑是最生动的。Aviator,QLExpress,MVEL在国际很受欢迎,QLExpress有阿里背书。
代码大小和依赖方面,Aviator,MVEL依赖少,并且代码大小也偏小。
性能方面,假设你经常使用表白式引擎口头字面量算术计算或方法调用偏多可以决定SpEl,MVEL。假设宿愿全体性能体现较好可以决定Aviator。
安保方面,假设想自定义安保选项,可以思考Aviator,QLExpress和JEXL。
经常使用案例方面,Aviator,MVEL,QLExpress在国际都有实践经常使用案例可循。
语法方面,或许存在一些客观要素,仅供参考,团体感觉MVEL、JEXL的语法设计经常使用起来会更容易一些。
经过对以上几个方面的评价和剖析,宿愿可以协助团队基于自身状况及偏好决定最适宜自己名目的Java表白式引擎。
参考资料[1]QLExpress:[2]Aviator:[3]MVEL:[4]OGNL:[5]SpEl:
[6]Janino:
[7]JUEL:
[8]JEXL:
[9]Fel:
[10]ik-expression:
[11]JSEL:
[12]JMH:
求大神帮忙,写一个跟女生表白的程序,最好是java语言写的
额,网上有很多类似的图片。 比如双击之后,出来一个男的,拿着鲜花,说一句我爱你之类的,也可以直接出来一句话,表白就好了。
java 规则引擎哪个好
国内的厂商,旗正,做了十几年了,性价比可以,研究学习选drools,土豪选ODM。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。