目录
一、概述
中文spring官网:https://itmyhome.com/spring/expressions.html
英文spring官网:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions
Spring Expression Language
(简称SpEL)是一种功能强大的表达式语言,是
spring
提供的,该语言类似于JSP当中的EL表达式。但提供了很多额外的功能,最出色的就是
函数调用
和
简单字符串的模板函数
。他需要使用
spring
提供的解析器来解析,但是他不依赖于
spring
,可以独立使用。在
spring
程序当中,我们不用管解析器,由
spring
来帮我们自动构建。我们只需要写想要表达的字符串,交给
spring
来进行解析即可。
什么地方会用到SpEL表达式?
使用SpEL表达式的地方应该有很多,据我目前了解的有以下三种情况可以使用:
- @Value注解可以通过
@Value(“${xxxx}”)
的形式来获取application当中的配置,使用@Value(“#{xxxx}”)
的形式可以使用SpEL表达式 - spring cache当中@Cache相关的注解当中的key属性值可以使用SpEL表达式
@Override@Cacheable(value ="rbac:roleSet", key ="T(org.apache.commons.lang3.StringUtils).join(#roles,'|')", unless ="#result == null || #result.size() == 0")publicList<String>getRoleIdsByRole(Set<String> roles){returnnull;}
- xml当中bean标签下property属性注入,是利用属性set方法注入的,然后使用spel表达式也是
#{ }
<beanid="numberGuess"class="org.spring.samples.NumberGuess"><propertyname="randomNumber"value="#{ T(java.lang.Math).random() * 100.0 }"/><!-- other properties --></bean><!--numberGuess.randomNumber相当于是从容器当中获取numberGuess的randomNumber 属性 --><beanid="shapeGuess"class="org.spring.samples.ShapeGuess"><propertyname="initialShapeSeed"value="#{ numberGuess.randomNumber }"/><!-- other properties --></bean>
二、SpEL解析器
有时候不知道输入的SpEL表达式对不对,可以通过如下方式来进行测试,其中
'Hello World'.concat('!')
就是输入的表达式
publicstaticvoidmain(String[] args){// 创建spel表达式分析器ExpressionParser parser =newSpelExpressionParser();// 输入表达式Expression exp = parser.parseExpression("'Hello World'.concat('!')");// 获取表达式的输出结果,getValue入参是返回参数的类型String value = exp.getValue(String.class);System.out.println(value);}
'Hello World'.concat('!')
相当于在Java当中的
"Hello World".concat("!")
,Java当中字符串是使用的双引号
"字符串"
,而SpEL表达式使用的是单引号,
concat
就是
String
当中的一个拼接方法,SpEL支持调用方法的。输出结果:
使用new这种表达式也是可以的:
// toUpperCase转换为大写字符Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");
三、代码示例
3.1 使用某个对象的属性
publicstaticvoidmain(String[] args){Student student =newStudent("张三",11);ExpressionParser parser =newSpelExpressionParser();Expression exp = parser.parseExpression("name");String name =(String) exp.getValue(student);System.out.println(name);}
根据属性判断是否是某个值
publicstaticvoidmain(String[] args){Student student =newStudent("张三",11);ExpressionParser parser =newSpelExpressionParser();// 判断student对象当中的name属性是否是张三Expression exp = parser.parseExpression("name == '张三'");Boolean name =(Boolean) exp.getValue(student);System.out.println(name);}
在spring项目当中,我们想要获取某个对象的属性只需要
#{容器当中对象的名称.属性名}
3.2 systemProperties
在spring项目当中变量
systemProperties
是预定义的,可以通过
#{systemProperties['属性名']}
来获取值,systemProperties就是一个系统类,可以获取到jdk版本,系统相关的属性。
publicclassSystemProperties{publicstaticvoidmain(String[] args){Properties properties =System.getProperties();Iterator<Map.Entry<Object,Object>> iterator = properties.entrySet().iterator();while(iterator.hasNext()){Map.Entry<Object,Object> entry = iterator.next();System.out.println(entry.getKey()+"==="+ entry.getValue());}}}
@Value("#{systemProperties['user.language']}")privateString name;
四、表达式语言
表达式语言支持以下功能:
- 文字表达式: 字符串使用单引号
'字符串'
,同时还支持double、int、boolean、Object类型,这些类型都不需要引号。 - 布尔和关系运算符: 支持的逻辑运算符 and, or, and not
- 类表达式: T(全类名),java.lang类型不需要是 完全限定,使用方式:
T(String)
,T(java.util.Date)
,也可以通过这种方式来调用方法。 - 访问 properties, arrays, lists, maps: 只要用一个
.
表示嵌套 属性值,属性名称的第一个字母不区分大小写。- 数组:inventions[3]
- List当中存储的对象,获取对象属性:Members[0].Name
,属性值是个数组的情况:Members[0].Inventions[6]
- properties和maps,获取指定key值:Officers['president']
,key值假如是个对象,获取对象当中属性的值,Officers['president'].PlaceOfBirth.City
,假如是数组Officers['advisors'][0].PlaceOfBirth.Country
- 方法调用:
'abc'.substring(2, 3)
,isMember('Mihajlo Pupin')
,这两种都是可以的,一种是通过某个对象调用方法,一种是直接调用当前类的方法。 - 关系运算符:
'black' < 'block'
,2 < -5.0
,2 == 2
,除了标准的关系运算符SpEL支持instanceof和 增则表达式的matches操作。'xyz' instanceof T(int)
,'5.00' matches '^-?\\d+(\\.\\d{2})?$'
。每个符号操作者也可以被指定为一个纯字母变量。这个 避免了在使用的符号有特殊含义的文档类型的问题 其表达被嵌入(例如,XML文档)。文本是等值 比如: lt (<), gt (>), le (<=), ge (>=), eq (==), ne (!=), div (/), mod (%), not (!). 这些都是不区分大小写。 - 逻辑运算符: 支持的逻辑运算符 and, or, and not,示例:
isMember('Nikola Tesla') or isMember('Albert Einstein')
- 数学运算符: 加法运算符可以用于数字和字符串。减法,乘法 和除法只能在数字被使用。支持其他数学运算符 模量(%)和指数幂(^)。标准的运算符优先级执行。
- 调用构造函数: 构造函数可以使用new运算符调用。
new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')
- Bean引用: 如果解析上下文已经配置,那么bean解析器能够 从表达式使用(@)符号查找bean类。例如:
@foo
- 构造Array:
new int[]{1,2,3}
,@Value("#{new int[]{1,2,3}}")
- 内嵌lists:
{1,2,3,4}
代表List,在spring项目当中使用的话就得再嵌套一层,@Value("#{{1,2,3,4}}")
,list的属性假如也是list可以使用{{'a','b'},{'x','y'}}
,@Value("#{{{'a','b'},{'x','y'}}}")
层次关系一定要屡明白! - 内嵌maps:
{name:'Nikola',dob:'10-July-1856'}
,{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}
- 三元运算符:
false ? 'trueExp' : 'falseExp'
,Elvis操作符使三元运算符语法的缩短,并用于在 Groovy语言。 Java当中的三元:String displayName = name != null ? name : "Unknown";
,SpEL当中使用Elvis操作符:null?:'Unknown'
,@Value("#{systemProperties['pop3.port'] ?: 25}")
如果它不存在,那么将定义为25 - 变量: 变量可以在使用语法
#变量名
表达引用。变量使用在StandardEvaluationContext
方法的setVariable
设置。变量#this
始终定义和指向的是当前的执行对象,变量#root总是 定义和指向root context object。虽然#this
可能作为表达式的一些组件被执行 ,但#root
总是指 root。
// create an array of integersList<Integer> primes =newArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));// create parser and set variable primes as the array of integersExpressionParser parser =newSpelExpressionParser();StandardEvaluationContext context =newStandardEvaluationContext();
context.setVariable("primes",primes);// all prime numbers > 10 from the list (using selection ?{...})// evaluates to [11, 13, 17]List<Integer> primesGreaterThanTen =(List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context);
#root
我们在使用spring cache的时候经常会用到:
- bean引用: 如果解析上下文已经配置,那么bean解析器能够 从表达式使用(@)符号查找bean类。
- 安全导航运算符: 安全导航操作符是用来避免NullPointerException,用法:
PlaceOfBirth?.City
,代表的是获取PlaceOfBirth对象的City属性。假如PlaceOfBirth为null正常会报空指针,该 安全航行运算符将简单地返回空
代替抛出的异常。 - 用户定义的函数: 支持自定义函数,函数就是方法。
- 集合投影: 投影允许集合驱动子表达式和解析 生成一个新的集合。
Members.![placeOfBirth.city]
一个map也可以用于驱动投影。 - 集合筛选: 选择是一个强大的表达式语言功能,他允许你转换一些 源集合到另一个通过其条目选择。
Members.?[Nationality == 'Serbian']
,map.?[value<27]
说白了就是过滤功能 - 模板表达式: 表达式模板允许文字文本与一个或多个解析块的混合。 你可以每个解析块分隔前缀和后缀的字符, 当然,常见的选择是使用#{}作为分隔符。
版权归原作者 怪 咖@ 所有, 如有侵权,请联系我们删除。