SpEL表达式学习笔记
SpEL表达式学习笔记
1. 介绍
Spring表达式语言(简称SpEl)是一个支持查询和操作运行时对象的功能强大的表达式语言。
Spel 创建的初衷是了给 Spring 社区提供一种简单而高效的表达式语言,一种可贯穿整个 Spring 产品组的语言。这种语言的特性应基于 Spring 产品的需求而设计。
2. 功能和案例
2.1 文字表达式
支持简单的变量。
package me.int32.test.spel;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
public static void main(String[] args) {
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("'hello world'");
System.out.println(expression.getValue());
}
}
// >> hello world
SpEL支持很多功能特性,如调用方法,访问属性,调用构造函数。
package me.int32.test.spel;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
public static void main(String[] args) {
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("'hello world'.concat('!')");
System.out.println(expression.getValue());
}
}
// >>> hello world!
package me.int32.test.spel;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
public static void main(String[] args) {
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("new String('hello world').toUpperCase()");
System.out.println(expression.getValue());
}
}
// >>> HELLO WORLD
SpEL可以获取bean对象的属性, 注意,必须是标准bean才可以.
package me.int32.test.spel;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Main {
static class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
User user = new User("gc", 23);
EvaluationContext context = new StandardEvaluationContext(user);
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("name");
System.out.println(expression.getValue(context));
user.setName("gc2");
System.out.println(expression.getValue(context));
}
}
// >>> gc
// >>> gc2
上面这种方式,跟下面这种方式,达到的效果是一样的。
package me.int32.test.spel;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Main {
static class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
User user = new User("gc", 23);
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("name");
System.out.println(expression.getValue(user));
user.setName("gc2");
System.out.println(expression.getValue(user));
}
}
// >>> gc
// >>> gc2
那上面两种区别在哪呢? 区别在于如果需要重复使用一个对象,可以构造一个StandardEvaluationContext
, 它本身会有一些优化,虽然构造它比较昂贵,但使用起来会更快,如果只使用一次那可以不用它,如果会重复使用,建议使用它。
当然,SpEL表达时可以修改对象内的值。
package me.int32.test.spel;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Main {
static class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
User user = new User("gc", 23);
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("name");
System.out.println(expression.getValue(user));
expression.setValue(user, "gc2");
System.out.println(expression.getValue(user));
}
}
// >>> gc
// >>> gc2
2.2 表达时支持定义bean
public static class FieldValueTestBean{
@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;
public void setDefaultLocale(String defaultLocale){
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale(){
return this.defaultLocale;
}
}
public static class PropertyValueTestBean{
private String defaultLocale;
@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale){
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
public class SimpleMovieLister{
private MovieFinder movieFinder;
private String defaultLocale;
@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}
}
2.3 文字表达式
ExpressionParser parser = new SpelExpressionParser();
// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
Object nullValue = parser.parseExpression("null").getValue();
Numbers 支持 负号、科学技数法、小数点等,默认使用 Double.parseDouble()
解析
2.4 属性,数组,List,Map,索引
package me.int32.test.spel;
import com.google.common.collect.Maps;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) {
User user = new User("gc", 23, new String[]{"123456"}, Arrays.asList("中国 上海", "中国 北京"), new HashMap<>());
System.out.println(new SpelExpressionParser().parseExpression("age + 10")
.getValue(user, Integer.class)); // 属性
System.out.println(new SpelExpressionParser().parseExpression("phone[0]")
.getValue(user, String.class)); // 数组
System.out.println(new SpelExpressionParser().parseExpression("address[0]")
.getValue(user, String.class)); // 列表
System.out.println(new SpelExpressionParser().parseExpression("[0].phone[0]")
.getValue(new User[]{user}, String.class)); // 数组
System.out.println(new SpelExpressionParser().parseExpression("has['书包'] == null")
.getValue(user, Boolean.class)); // map
}
}
// >>> 33
// >>> 123456
// >>> 中国 上海
// >>> 123456
// >>> true
2.5 内联
public static void main(String[] args) {
System.out.println(new SpelExpressionParser().parseExpression("{1,2,3,4,5}").getValue());
System.out.println(new SpelExpressionParser().parseExpression("new int[]{1,2,3,4,5}").getValue());
}
// >>> [1, 2, 3, 4, 5]
// >>> [I@5f5a92bb
2.6 方法调用
// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);
// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);
2.7 操作符
package me.int32.test.spel;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) {
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
System.out.println(spelExpressionParser.parseExpression("1 < 3").getValue());
System.out.println(
spelExpressionParser.parseExpression("T(java.lang.Math).random() < T(java.lang.Math).random()")
.getValue());
System.out.println(spelExpressionParser.parseExpression("'black' < 'apple'").getValue());
System.out.println(spelExpressionParser.parseExpression("'xyz' instanceof T(String)").getValue());
System.out.println(spelExpressionParser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue());
System.out.println(spelExpressionParser.parseExpression("true and false").getValue());
System.out.println(spelExpressionParser.parseExpression("true and !false").getValue());
System.out.println(spelExpressionParser.parseExpression("1 + 2 - 3 + 4 * 5 * (6 + 7)").getValue());
}
}
// >>> true
// >>> true
// >>> false
// >>> true
// >>> false
有时候为了避免产生歧义,可以使用嵌入式操作符: ` lt ('<'), gt ('>'), le ('<='), ge ('>='), eq ('=='), ne ('!='), div ('/'), mod ('%'), not ('!')`.
2.8 类型
T
操作能够获取到 java.lang.Class
,可以直接调用静态方法,StandardEvaluationContext
已经内置了java.lang
包,也就是说T()
在表示java.lang
的时候,不需要指定包路径,其他的路径,必须手动指定全。
package me.int32.test.spel;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) {
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
System.out.println(spelExpressionParser.parseExpression("T(String)").getValue(Class.class));
System.out.println(spelExpressionParser.parseExpression("T(java.util.Date)").getValue(Class.class));
System.out.println(spelExpressionParser.parseExpression("T(Math).random()").getValue());
System.out.println(spelExpressionParser.parseExpression(
"T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
.getValue(Boolean.class));
}
}
// >>> class java.lang.String
// >>> class java.util.Date
// >>> 0.39669349863302106
// >>> true
2.9 构造器
能够调用构造器,除了原生类型喝String之外,必须写明类的全限定名。
package me.int32.test.spel;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) {
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
System.out.println(spelExpressionParser.parseExpression(
"new java.lang.StringBuffer().append('hello,').append('world!').toString()").getValue(
java.lang.String.class));
new Random().nextInt(10);
System.out.println(spelExpressionParser.parseExpression(
"(new java.util.Random().nextInt(10)) < (new java.util.Random().nextInt(10))")
.getValue(Boolean.class));
}
}
// >>> hello,world!
// >>> true
2.10 变量
- 支持设置变量到上下文里面
#this
总是指定当前对象#root
总是指定到对象
package me.int32.test.spel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("name", "gc");
User user = new User("gc2", 10, new String[]{"123"}, new ArrayList<String>() , new HashMap<>());
context.setRootObject(new ArrayList<User>() );
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
System.out.println(spelExpressionParser.parseExpression(
"#root[0].name = #name").getValue(context));
System.out.println(user);
}
}
// >>> gc
// >>> Main.User(name=gc, age=10, phone=[123], address=[hello world!], has={})
2.11 函数调用
StandardEvaluationContext
支持注册函数,执行函数调用
package me.int32.test.spel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) throws NoSuchMethodException {
StandardEvaluationContext context = new StandardEvaluationContext();
context.registerFunction("reverseString", Main.class.getDeclaredMethod(
"reverseString", String.class
));
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
System.out.println(spelExpressionParser.parseExpression(
"#reverseString('hello world')").getValue(context));
}
public static String reverseString(String input) {
StringBuilder backwards = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
backwards.append(input.charAt(input.length() - 1 - i));
}
return backwards.toString();
}
}
// >>> dlrow olleh
2.12 Bean 引用
如果在执行上下文中已经配置好了bean解析器,可以使用@
来使用查找和使用bean
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);
2.13 If-Then-Else
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"
2.14 三元操作符优化
ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("#name?:'Unknown'").getValue(String.class);
// 等价:String displayName = name != null ? name : "Unknown";
System.out.println(name);
// >>> 'Unknown'
下面是一些更复杂的例子
ExpressionParser parser = new SpelExpressionParser();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);
System.out.println(name);
tesla.setName(null);
name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);
System.out.println(name);
// >>> Nikola Tesla
// >>> Elvis Presley
2.15 针对NPE优化的操作符
ExpressionParser parser = new SpelExpressionParser();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
System.out.println(city);
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
System.out.println(city);
// >>> Smiljan
// >> null
2.16 容器选择
package me.int32.test.spel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) throws NoSuchMethodException {
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
System.out.println(
spelExpressionParser.parseExpression("#root.?[#this > 10]")
.getValue(new ArrayList<Integer>() ));
System.out.println(
spelExpressionParser.parseExpression("#root.?[#this.value > 10]")
.getValue(new HashMap<String, Integer>() ));
}
}
// >>> [11, 12]
// >>> {gc=11}
map 每一个元素是 Entry
2.17 容器映射
package me.int32.test.spel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) throws NoSuchMethodException {
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
User user1 = new User("gc1", 3, null, null, null);
User user2 = new User("gc2", 11, null, null, null);
User user3 = new User("gc3", 13, null, null, null);
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
userList.add(user3);
System.out.println(spelExpressionParser.parseExpression(
"#root.?[#this.age > 10].![new java.lang.StringBuffer().append(#this.name + '投影').toString()]").getValue(userList));
}
}
// >>> [gc2投影, gc3投影]
2.18 表达式模版
package me.int32.test.spel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Main {
@Data
@AllArgsConstructor
static class User {
private String name;
private int age;
private String[] phone;
private List<String> address;
private Map<String, Integer> has;
}
public static void main(String[] args) throws NoSuchMethodException {
SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
User user = new User("gc", 3, null, null, null);
System.out.println(spelExpressionParser.parseExpression(
"'hello ' + #root.name")
.getValue(user));
System.out.println(spelExpressionParser.parseExpression(
"hello #{#root.name}", new TemplateParserContext())
.getValue(user));
System.out.println(spelExpressionParser.parseExpression(
"hello !{#root.name}!", new TemplateParserContext("!{", "}!"))
.getValue(user));
}
}
// >>> hello gc
// >>> hello gc
// >>> hello gc