SpEL是Spring提供的一种的表达式语言,支持在运行时查询和操作对象。
SpEL并不直接与Spring相关联,可以独立使用。
SpEL解析器
SpEL提供了对应的解析器SpelExpressionParser
用于解析SpEL表达式
EvaluationContext
EvaluationContext
用于计算表达式以解析属性、方法、字段,并帮助执行类型转换。
Spring提供了两个实现SimpleEvaluationContext
和StandardEvaluationContext
表达式类型
Literal Expressions 文字表达式
文字表达式支持字符串、数值、布尔值、空值。字符串由单引号进行引用,要将单引号本身放在字符串中,需使用两个单引号。
数字类型支持使用负号、指数表示法、小数点,默认使用Double.parseDouble()解析数值
ExpressionParser parser = new SpelExpressionParser();
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();
System.out.println(helloWorld); // Hello World
String word = (String) parser.parseExpression("'''word'''").getValue();
System.out.println(word); // 'word'
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
System.out.println(avogadrosNumber); // 6.0221415E23
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();
System.out.println(maxValue); // 2147483647
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
System.out.println(trueValue); // true
Object nullValue = parser.parseExpression("null").getValue();
System.out.println(nullValue==null); // true
Properties, Arrays, Lists, Maps, and Indexers 属性,数组,列表,Map,索引
通过点进行属性获取,嵌套属性使用点对属性进行分隔
properties 属性
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class TestVo{
private String name;
private int age;
private Date birth;
private Addr addr;
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class Addr{
private String province;
private String city;
}
TestVo testVo = new TestVo("young", 20, new Date(), new Addr("beijing", "beijing"));
ExpressionParser parser = new SpelExpressionParser();
int year = (int) parser.parseExpression("birth.year+1900").getValue(testVo);
System.out.println(year); // 2021
String city = (String) parser.parseExpression("addr.city").getValue(testVo);
System.out.println(city); //北京
数组、列表
数组,列表,可以通过[]
指定下标来获取对应的值,其余与properties一样
@AllArgsConstructor
@Data
public static class TestList{
private List<Integer> listField;
}
List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList()); // 0-9
int[] arr = IntStream.range(0, 10).toArray(); // 0-9
ExpressionParser parser = new SpelExpressionParser();
System.out.println(parser.parseExpression("[5]").getValue(list));// 5
System.out.println(parser.parseExpression("[9]").getValue(arr)); // 9
TestList testList = new TestList(list);
System.out.println(parser.parseExpression("listField[4]").getValue(testList)); // 4
Map
map的内容是通过[]
中指定key来获取值的,对象类型也可以用同样的方式获取,字符串类型的key要用单引号引起来
TestVo testVo = new TestVo("young", 20, new Date(), new Addr("shanxi", "xi'an"));
ExpressionParser parser = new SpelExpressionParser();
System.out.println(parser.parseExpression("['name']").getValue(testVo)); // young
System.out.println(parser.parseExpression("addr['city']").getValue(testVo)); // xi'an
Map<String,String> map = new HashMap<>();
map.put("beijing","beijing");
map.put("shanxi","xi'an");
map.put("jiangsu","南京");
System.out.println(parser.parseExpression("['jiangsu']").getValue(map)); // 南京
Inline Lists 内联列表
可以使用{}
直接在表达式中表示列表,{}
表示一个空列表,如果完全由固定文字组成,则SpEL会创建一个列表常量
ExpressionParser parser = new SpelExpressionParser();
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue();
System.out.println(numbers); // [1, 2, 3, 4]
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue();
System.out.println(listOfLists); // [[a, b], [x, y]]
Inline Maps 内联Map
可以使用{key:value}
直接在表达式中表示Map,{:}
表示一个空Map
ExpressionParser parser = new SpelExpressionParser();
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue();
System.out.println(inventorInfo); // {name=Nikola, dob=10-July-1856}
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue();
System.out.println(mapOfMaps); // {name={first=Nikola, last=Tesla}, dob={day=10, month=July, year=1856}}
Array Construction 构建数组
可以使用Java的数组构建语法构建表示一个数组,构建多维数组时,不能进行初始化操作
ExpressionParser parser = new SpelExpressionParser();
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue();
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue();
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue();
Methods 方法
可以调用java的方法,支持可变参数
public static class TestMethod{
public boolean isString(Object obj){
return obj!=null && obj.getClass().equals(String.class);
}
}
ExpressionParser parser = new SpelExpressionParser();
System.out.println(parser.parseExpression("'Hello World'.concat('!')").getValue()); // Hello World!
System.out.println(parser.parseExpression("'Hello World'.bytes.length").getValue()); // 11
System.out.println(parser.parseExpression("new String('hello world').toUpperCase()").getValue()); // HELLO WORLD
System.out.println(parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class)); // bc
TestMethod testMethod = new TestMethod();
System.out.println(parser.parseExpression("isString(123)").getValue(testMethod)); // false
System.out.println(parser.parseExpression("isString('123')").getValue(testMethod));// true
Operators 运算符
关系运算符
支持等于,不等于,大于,大于等于,小于,小于等于这类标准关系运算符,还支持instanceof
和基于正则表达式的matches
运算
对于null值,任何非null的值都大于null,及x>null
恒等于true,x<null
恒等于false
ExpressionParser parser = new SpelExpressionParser();
System.out.println(parser.parseExpression("2 == 2").getValue(Boolean.class)); // true
System.out.println(parser.parseExpression("2 < -5.0").getValue(Boolean.class)); // false
System.out.println(parser.parseExpression("'black' < 'block'").getValue(Boolean.class)); // true
System.out.println(parser.parseExpression("'black' == 'black'").getValue(Boolean.class)); // true
System.out.println(parser.parseExpression("'xyz' instanceof T(Integer)").getValue(Boolean.class)); // false
System.out.println(parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class)); // true
System.out.println(parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class)); // false
System.out.println(parser.parseExpression("1 instanceof T(int)").getValue(Boolean.class)); // false
System.out.println(parser.parseExpression("1 instanceof T(Integer)").getValue(Boolean.class));// true
SpEL中 T表示类型
基本类型会被处理成包装类,所以instanceof int为false
为了避免符号对嵌入表达式的文档类型(如xml)有特殊含义是,表达式可以使用转义符,且不区分大小写
- lt (<)
- ge (>)
- le (<=)
- ge (>=)
- eq (==)
- ne (!=)
- div (/)
- mod (%)
- not (!)
逻辑运算符
- and (&&)
- or (||)
- not (!)
ExpressionParser parser = new SpelExpressionParser();
TestMethod testMethod = new TestMethod();
System.out.println(parser.parseExpression("true and false").getValue(Boolean.class)); // false
String expression = "isString('abc') and isString(123)";
System.out.println(parser.parseExpression(expression).getValue(testMethod, Boolean.class)); // false
System.out.println(parser.parseExpression("true or false").getValue(Boolean.class)); // true
expression = "isString('abc') or isString(123)";
System.out.println(parser.parseExpression(expression).getValue(testMethod, Boolean.class)); // true
System.out.println(parser.parseExpression("!true").getValue(Boolean.class)); // false
expression = "isString('abc') and !isString(123)";
System.out.println(parser.parseExpression(expression).getValue(testMethod, Boolean.class)); // true
数学运算符
可以对数字和字符串使用加法运算符(+
),对数字可以使用减法(-
),乘法(*
)、除法(/
)、取模(%
),指数幂(^
)运算,按标准运算符优先级进行执行
ExpressionParser parser = new SpelExpressionParser();
System.out.println(parser.parseExpression("1 + 1").getValue(Integer.class)); // 2
System.out.println(parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class)); // test string
System.out.println(parser.parseExpression("1 - -3").getValue(Integer.class)); // 4
System.out.println(parser.parseExpression("1000.00 - 1e4").getValue(Double.class)); // -9000.0
System.out.println(parser.parseExpression("-2 * -3").getValue(Integer.class)); // 6
System.out.println(parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class)); // 24.0
System.out.println(parser.parseExpression("6 / -3").getValue(Integer.class)); // -2
System.out.println(parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class)); // 1.0
System.out.println(parser.parseExpression("7 % 4").getValue(Integer.class)); // 3
System.out.println(parser.parseExpression("8 / 5 % 2").getValue(Integer.class)); // 1
System.out.println(parser.parseExpression("1+2-3*8").getValue(Integer.class)); // -21
赋值运算符
设置属性,可以使用赋值运算符=
,可以使用getValue和setValue两种方法
ExpressionParser parser = new SpelExpressionParser();
TestVo testVo = new TestVo();
parser.parseExpression("name").setValue(testVo, "young");
System.out.println(testVo); // TestMain.TestVo(name=young, age=0, birth=null, addr=null)
String name = parser.parseExpression(
"name = 'jordan'").getValue(testVo, String.class);
System.out.println(name); // jordan
System.out.println(testVo); //TestMain.TestVo(name=jordan, age=0, birth=null, addr=null)
Types 类
可以使用T
运算符来指定java.lang.Class
的实例,静态方法也通过此运算符调用。除了java.lang
包下的类,其余的类均需使用全限定类名
ExpressionParser parser = new SpelExpressionParser();
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class);
String value = parser.parseExpression("T(String).valueOf(100)").getValue(String.class); // 100
Constructors 构造函数
可使用new调用构造函数,除了java.lang
包中的类型,其他类型都应该写全限定类名
Variables 变量
可以使用#variableName
引用变量,变量是使用实现setVariable
上的方法设置的EvaluationContext
变量名规则由以下描述的一个或多个字符组成
- 字母A到Z或a到z
- 数字0到9
- 下划线 _
- 美元符号 $
ExpressionParser parser = new SpelExpressionParser();
SimpleEvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("testName","young");
TestVo testVo = new TestVo();
parser.parseExpression("name = #testName").getValue(context,testVo);
System.out.println(testVo); // TestMain.TestVo(name=young, age=0, birth=null, addr=null)
#this和#root
#root 表示跟对象,可以通过EvaluationContext
设置或在调用方法时指定
#this 表示当前变量
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
List<Integer> primes = new ArrayList<>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
context.setVariable("primes", primes);
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context); // [11, 13, 17]
System.out.println(primesGreaterThanTen);
Map<String,Object> map = new HashMap<>();
map.put("code",5);
context.setVariable("primesMap", map);
// context.setRootObject(map);
System.out.println(parser.parseExpression("#primes.?[#this>#root['code']]").getValue(context,map)); // [7, 11, 13, 17]
Functions 方法
可以自定义方法来扩展SpEL,通过EvaluationContext
进行注册,被注册的方法必须是被static
修饰
public static class TestMethod{
public static boolean isString(Object obj){
return obj!=null && obj.getClass().equals(String.class);
}
}
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", TestMethod.class.getDeclaredMethod("isString", Object.class));
ExpressionParser parser = new SpelExpressionParser();
System.out.println(parser.parseExpression("#myFunction(123)").getValue(context)); // false
Bean References Bean引用
如果EvaluationContext
配置了BeanResolver
,则可以通过@
符号加bean名称获取bean,如果要访问工厂Bean(FactoryBean
),需在bean名称前加一个&
符号
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@something").getValue(context);
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("&foo").getValue(context);
Ternary Operator (If-Then-Else) 三目运算符
String falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String.class); // falseExp
parser.parseExpression("name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");
expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
"+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";
String queryResultString = parser.parseExpression(expression)
.getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla is a member of the IEEE Society"
Elvis Operator Elvis运算符
Elvis 运算符是三元运算符语法的缩写,来源于Groovy
语言
语法:变量?:返回值
如果变量是null,则返回返回值,否则返回变量本身
ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("name?:'Unknown'").getValue(new TestVo(), String.class);
System.out.println(name); // 'Unknown'
TestVo testVo = new TestVo("young",0,null,null);
System.out.println(parser.parseExpression("name?:'jordan'").getValue(testVo, String.class)); // young
testVo.setName(null);
System.out.println(parser.parseExpression("name?:'jordan'").getValue(testVo, String.class)); // jordan
可以使用这种方法设置默认值
Safe Navigation Operator 安全导航运算符
用于避免空指针操作,来源于Groovy
语言
语法:对象?.属性
当对象为null时,返回null,否则取属性中的值,有点类似于Optional.ofNullable(对象).map(对象.属性).orElse(null)
的操作
ExpressionParser parser = new SpelExpressionParser();
TestVo testVo = new TestVo("young", 0, new Date(), new Addr("shanxi", "xian"));
System.out.println(parser.parseExpression("addr?.city").getValue(testVo, String.class)); // xian
testVo.setAddr(null);
System.out.println(parser.parseExpression("addr?.city").getValue(testVo, String.class)); // null
Collection Selection 集合查找
使用.?[selectionExpression]
进行集合过滤,返回一个满足selectionExpression
的新集合
数组,map或者实现了java.lang.Iterable的类型都支持
对于数组或列表,将针对每个元素进行过滤
对于map,将基于Map.Entry进行过滤,可以通过Entry的key或者value进行查找
可以通过.^[selectionExpression]
获取第一个查找到的第一个元素,.$[selectionExpression]
获取最后一个元素
ExpressionParser parser = new SpelExpressionParser();
SimpleEvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
List<Integer> list = Arrays.asList(1, 3, 5, 7, 9, 11, 13, 15, 17, 19);
context.setVariable("list",list);
System.out.println(parser.parseExpression("#list.?[#this<10]").getValue(context)); // [1, 3, 5, 7, 9]
TestSelector testSelector = new TestSelector(list);
System.out.println(parser.parseExpression("code.?[#this<7]").getValue(testSelector)); // [1, 3, 5]
Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(e -> e, e -> e));
context.setVariable("map",map);
System.out.println(parser.parseExpression("#map.?[value>11]").getValue(context)); // {17=17, 19=19, 13=13, 15=15}
System.out.println(parser.parseExpression("#map.^[value>15]").getValue(context));// {17=17}
System.out.println(parser.parseExpression("#map.$[value>15]").getValue(context));// {15=15}
Collection Projection
使用.![projectionExpression]
,将结果表达式里的数据生成一个新的集合
ExpressionParser parser = new SpelExpressionParser();
List<TestVo> list = new ArrayList<>();
list.add(new TestVo("1",1,new Date(),new Addr("shanxi","xian")));
list.add(new TestVo("2",2,new Date(),new Addr("jingsu","nanjing")));
list.add(new TestVo("3",3,new Date(),new Addr("beijing","beijing")));
SimpleEvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("list",list);
System.out.println(parser.parseExpression("#list.![addr.city]").getValue(context));
有点类似于java的Stream操作
list.stream().map(e -> e.getAddr().getCity()).collect(Collectors.toList())