spring expression language概念spring expression language(简称spel)是一种强大的表达式语言,支持在运行时查询和操作对象图。该语言的语法类似于unified el,但提供了额外的特性, 最显著的是方法调用和基本的字符串模板功能。
虽然还有其他几种java表达式语言可用——ognl、mvel和jboss el等,spel是为了向spring社区提供一种受良好支持的表达式语言,可以跨spring产品组合中的所有产品使用。spel基于一种与技术无关的api,在需要时可以集成其他表达式语言实现。
作用基于spel我们可以实现下面这些功能,当然是基于表达式,spel提供了表达式运行环境,而且不依赖于spring,这样我可以基于其强大的执行器实现各种扩展。
支持功能 字面表达式布尔和关系运算符正则表达式类表达式访问属性、数组、列表和映射方法调用关系运算符申明调用构造函数bean引用数组构造内联的list内联的map三元运算符变量用户自定义函数集合投影集合选择模板化表达式关键接口将表达式字符串解析为可求值的编译表达式。支持解析模板以及标准表达式字符串。
public interface expressionparser { /** * 解析字符串表达式为expression对象 */ expression parseexpression(string expressionstring) throws parseexception; /** * 解析字符串表达式为expression对象,基于parsercontext解析字符串,比如常见的#{exrp} */ expression parseexpression(string expressionstring, parsercontext context) throws parseexception;}表达式在求值上下文中执行。正是在这个上下文中,表达式求值期间遇到的引用才会被解析。evaluationcontext接口有一个默认的实现standardbeanexpressionresolver,可以通过继承该类进行扩展。
public interface evaluationcontext { /** * 获取root上下文对象 */ typedvalue getrootobject(); /** * 返回属性读写访问器 */ list getpropertyaccessors(); /** * 返回构造器解析器 */ list getconstructorresolvers(); /** * 返回方法解析器 */ list getmethodresolvers(); /** * 返回bean解析器,用于bean的查找 */ @nullable beanresolver getbeanresolver(); /** * 根据类名(一般为全限名)返回一个类型定位器,比如t(java.lang.math) */ typelocator gettypelocator(); /** * 返回可以将值从一种类型转换(或强制转换)为另一种类型的类型转换器。 */ typeconverter gettypeconverter(); /** * 返回一个类型比较器,用于比较对象对是否相等。 */ typecomparator gettypecomparator(); /** * 返回一个运算符重载器,该重载器可以支持多个标准类型集之间的数学运算。 */ operatoroverloader getoperatoroverloader(); /** * 为变量设值 */ void setvariable(string name, @nullable object value); /** * 从变量取值 */ @nullable object lookupvariable(string name);}适用场景扩展变量
正如spring中使用的那样,我们可以基于spel实现基于spring容器、运行环境上下文等动态取值。数据审计
通过从复杂的数据结构中进行数据运算,一般针对数据汇总或者复杂的结构化数据时,通过自定义特定表达式(类似于dsl)来实现数据运算。spring的使用在spring中有着大量使用spel的场景,在平时的开发中,可能会看到如下的这些配置,比如通过${}、#{}这些包裹的表达式,主要都是基于spel实现。
@value(#{systemproperties['pop3.port'] ?: 25})
简单的看下源吗,可以看到针对@value标记的类属性,是如何为其注入属性:autowiredannotationbeanpostprocessor -> autowiredfieldelement#injectvalue = beanfactory.resolvedependency(desc, beanname, autowiredbeannames, typeconverter)defaultlistablebeanfactory#doresolvedependency
public class defaultlistablebeanfactory { public object doresolvedependency(dependencydescriptor descriptor, @nullable string beanname, @nullable set autowiredbeannames, @nullable typeconverter typeconverter) throws beansexception { injectionpoint previousinjectionpoint = constructorresolver.setcurrentinjectionpoint(descriptor); try { class type = descriptor.getdependencytype(); object value = getautowirecandidateresolver().getsuggestedvalue(descriptor); if (value != null) { if (value instanceof string) { // 处理 ${} 占位替换 string strval = resolveembeddedvalue((string) value); beandefinition bd = (beanname != null && containsbean(beanname) ? getmergedbeandefinition(beanname) : null); // spel处理 value = evaluatebeandefinitionstring(strval, bd); } } // 省略... return result; }catch (exception e){ // ... } }}@cacheable(value=users, key=#p0)
具体实现可以阅读源码:org.springframework.cache.interceptor.cacheoperationexpressionevaluator@kafkalistener(topics = #{'${topics}'.split(',')})
具体实现可以阅读源码:org.springframework.kafka.annotation.kafkalistenerannotationbeanpostprocessor示例下面通过一些测试示例了解spel的基本用法,对应上面的一些功能实现,通过这些简单的例子,可以大概了解其语法与使用方式:
public class tests { @test public void runparser() throws nosuchmethodexception { spelexpressionparser parser = new spelexpressionparser(); map map = new hashmap(); map.put(name, spel); standardevaluationcontext context = new standardevaluationcontext(map); context.setvariable(var1, 1); context.setvariable(func1, stringutils.class.getmethod(hastext, string.class)); context.setvariable(func2, new root()); context.setbeanresolver(new mybeanresolver()); log.info(字面量:{}, parser.parseexpression('hello').getvalue(context)); log.info(对象属性:{}, parser.parseexpression('hello'.bytes).getvalue(context)); log.info(变量:{}, parser.parseexpression(#var1).getvalue(context)); log.info(调用方法:{}, parser.parseexpression('hello'.concat(' world')).getvalue(context)); log.info(静态方法:{}, parser.parseexpression(t(java.lang.system).currenttimemillis()).getvalue(context)); log.info(方法:{}, parser.parseexpression(#func1('1')).getvalue(context)); log.info(实例方法:{}, parser.parseexpression(#func2.print('2')).getvalue(context)); root root = new root(); root.list.add(0); parser.parseexpression(list[0]).setvalue(context, root, 1); log.info(设值:{}, root); log.info(root: {}, parser.parseexpression(#root).getvalue(context)); log.info(root 取值: {}, parser.parseexpression(#root[name]).getvalue(context)); log.info(root 取值: {}, parser.parseexpression([name]).getvalue(context)); log.info(this: {}, parser.parseexpression(#this).getvalue(context)); log.info(运算符: {}, parser.parseexpression(1+1).getvalue(context)); log.info(操作符: {}, parser.parseexpression(1==1).getvalue(context)); log.info(逻辑运算: {}, parser.parseexpression(true && false).getvalue(context)); parsercontext parsercontext = new parsercontext() { @override public boolean istemplate() { return true; } @override public string getexpressionprefix() { return #{; } @override public string getexpressionsuffix() { return }; } }; log.info(#{表达式}: {}, parser.parseexpression(#{1+1}, parsercontext).getvalue(context)); log.info(map: {}, parser.parseexpression({name:'nikola',dob:'10-july-1856'}).getvalue(context)); log.info(list: {}, parser.parseexpression({1,2,3,4}).getvalue(context)); log.info(array: {}, parser.parseexpression(new int[]{1,2,3}).getvalue(context)); log.info(instanceof: {}, parser.parseexpression('hello' instanceof t(integer)).getvalue(context, boolean.class)); log.info(regex: {}, parser.parseexpression('5.00' matches '^-?d+(.d{2})?$').getvalue(context, boolean.class)); log.info(三目运算: {}, parser.parseexpression(false ? 'trueexp' : 'falseexp').getvalue(context, string.class)); log.info(bean: {}, parser.parseexpression(@bean1).getvalue(context)); }}特殊处理t
通过t(class)指定类型,可以用来类型判断或者调用类静态方法sec.settypelocator(new standardtypelocator(evalcontext.getbeanfactory().getbeanclassloader()));@
通过@name获取beansec.setbeanresolver(new beanfactoryresolver(evalcontext.getbeanfactory()));&
通过&name获取beanfactorysec.setbeanresolver(new beanfactoryresolver(evalcontext.getbeanfactory()));
通过#{}标识表达式,主要在springbeanexpressionresolver resolver = beanfactory.getbeanexpressionresolver(); beanexpressioncontext context = new beanexpressioncontext(beanfactory, null);
从root取值:#root.name 或者 name(#root可以忽略) 从variables取值或方法:#var
扩展下面以一个示例看下spel在我们项目中的具体应用:
public class tests{ /** * 执行测试 */ @test public void runcalc(){ spelexpressionparser parser = new spelexpressionparser(); standardevaluationcontext context = new standardevaluationcontext(); context.setvariable(helper,new studenthelper()); context.setvariable(students, genstudents(100)); log.info(max score: {}, parser.parseexpression(#helper.max(#students)).getvalue(context, double.class)); log.info(min score: {}, parser.parseexpression(#helper.min(#students)).getvalue(context, double.class)); log.info(avg score: {}, parser.parseexpression(#helper.avg(#students)).getvalue(context, double.class)); } /** * 生成测试数据 * @param count * @return */ private list genstudents(int count){ list students = new arraylist(); faker faker = new faker(locale.china); name name = faker.name(); number number = faker.number(); intstream.range(0, count).foreach(i- >{ students.add(new student(name.name(), number.randomdouble(3, 60, 100))); }); return students; } @data @allargsconstructor class student{ private string name; private double score; } /** * 工具类 */ class studenthelper{ public double max(list students){ if(collectionutils.isempty(students)){ return 0; } return students.stream().maptodouble(student::getscore).max().getasdouble(); } public double min(list students){ if(collectionutils.isempty(students)){ return 0; } return students.stream().maptodouble(student::getscore).min().getasdouble(); } public double avg(list students){ if(collectionutils.isempty(students)){ return 0; } return students.stream().maptodouble(student::getscore).average().getasdouble(); } }}在这个示例中,我们主要通过spel获取获取学生分值的最大值、最小值、平均值等方式,在实际的项目中使用时,绝非如此简单,比如我们在做数据统计时,对各项指标数值的计算就是通过spel实现, 因为具体功能的实现是由我们自己定义,因此在业务扩展上会非常的方便。
结束语spel是一个功能非常强大的基于java的解释型语言解析器,如果你想基于表达式的形式,对复杂结构数据计算或审计的需求时,不妨试试这个轻量级工具。
联发科助力将5G智能手机推向大众市场、加快5G普及的优势
PSA集团的动力电池领域的布局有了新进展
如何构建合适的人工智能治理体系
二(2)输入端CMOS或非门电路
电路板在LED灯具上的作用
基于SpEL可以实现的功能
长安欧尚X5上市当天就突破2万,因为真正走进了年轻人的世界?
钽电解电容和陶瓷电容的区别
不同线制变送器的原理和布局
Netgear与HarmanKardon合作推出新款智能喇叭
如何制作Arduino控制的水火箭
Type-C集线器给您带来不同的功能,不通过的选择
研华更新ACP-7000 && ACP-5260配置
钢结构钣金折弯件3d扫描全尺寸偏差检测
广明源LED灯丝灯新结构成果获江门科学技术奖二等奖
交流点焊机焊接黑色金属的性能怎么样
病人监护系统中的各类传感器及其作用
机器视觉的光源基础知识的问答
虚拟货币交易所存在怎样的危机
关于接线端子的质量和可靠性检测的说明