Spring提供的SPEL表达式
SPEL
1. 概述
SpEL是Spring框架中用于表达式语言的一种方式。它类似于其他编程语言中的表达式语言,用于在运行时计算值或执行特定任务。
SpEL提供了一种简单且强大的方式来访问和操作对象的属性、调用对象的方法,以及实现运算、条件判断等操作。它可以被用于XML和注解配置中,可以用于许多Spring框架中的特性,如依赖注入、AOP、配置文件等。
SpEL表达式可以在字符串中进行定义,使用特殊的语法和符号来表示特定的操作。例如,可以使用${expression}来表示一个SpEL表达式,其中expression是具体的SpEL语句。
SpEL支持各种操作和函数,包括算术运算、逻辑运算、条件判断、正则表达式匹配、集合操作等。它还支持访问上下文中的变量和参数,以及调用对象的方法
2. 基础语法学习案例
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.util.*;
/**
* SPEL表达式案例
*
* @author xuyuan
* @see EvaluationContext
* @see SimpleEvaluationContext 定制化提供特定的上下文对象,控制读写权限,可以只读如下的SimpleEvaluationContext.forReadOnlyDataBinding().build()
* SimpleEvaluationContext.forReadOnlyDataBinding().build():
* 适用于简单的、只读的数据绑定场景。
* 性能较好,但功能有限。
* 不支持复杂的表达式操作。
* <p>
* new StandardEvaluationContext(obj):
* 适用于复杂的表达式求值场景。
* 功能全面,支持所有SPEL特性。
* 可以设置根对象和其他上下文信息,灵活性高。
*/
public class SpelExam {
/**
* 操作字面量
*/
@Test
public void test() {
ExpressionParser parser = new SpelExpressionParser();
// 获取字符串 "Hello World"
String helloWorld = parser.parseExpression("'Hello World'").getValue(String.class);
System.out.println(helloWorld);
// double类型 6.0221415E23
double avogadrosNumber = parser.parseExpression("6.0221415E+23").getValue(Double.class);
System.out.println(avogadrosNumber);
// int类型 2147483647
int maxValue = parser.parseExpression("0x7FFFFFFF").getValue(Integer.class);
System.out.println(maxValue);
// true
boolean trueValue = parser.parseExpression("true").getValue(Boolean.class);
System.out.println(trueValue);
// null
Object nullValue = parser.parseExpression("null").getValue();
System.out.println(nullValue);
}
/**
* 操作对象
*/
@Test
public void test2() {
// 定义Parser,可以定义全局的parser
ExpressionParser parser = new SpelExpressionParser();
// 注意!属性名的第一个字母不区分大小写。 birthdate.year等效于Birthdate.Year
// 取出Inventor 中,birthdate属性的year属性
Inventor zhangsan = new Inventor("zhangsan", new Date(), "China");
// 定义StandardEvaluationContext ,传入一个操作对象
StandardEvaluationContext zhangsanContext = new StandardEvaluationContext(zhangsan);
int year = parser.parseExpression("birthdate.year + 1900").getValue(zhangsanContext, Integer.class);
System.out.println(year); // 2025
// 取出Inventor的placeOfBirth的city属性
PlaceOfBirth placeOfBirth = new PlaceOfBirth("长沙", "中国");
zhangsan.setPlaceOfBirth(placeOfBirth);
String city = parser.parseExpression("placeOfBirth.City").getValue(zhangsanContext, String.class);
System.out.println(city); // 长沙
}
/**
* 操作数组和List
*/
@Test
public void test3() {
// 定义Parser,可以定义全局的parser
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// 省略数据初始化
Society ieee = new Society();
Inventor tesla = new Inventor("Tesla", "USA");
ieee.getMembers().add(tesla);
ieee.getMembers().add(new Inventor("BYD", "CN"));
ieee.getMembers().add(new Inventor("BMW", "GE"));
ieee.getMembers().add(new Inventor("FLL", "FR"));
tesla.setInventions(new String[]{"Lightning", "Telephone", "Computer", "Electricity", "Electric Motor", "Electric Engine", "Electric Car"});
String value = parser.parseExpression("inventions[3]").getValue(context, tesla, String.class);
System.out.println("取出tesla对象的inventions 第四个数据:" + value);
String name = parser.parseExpression("Members[0].Name").getValue(context, ieee, String.class);
System.out.println("取出ieee对象的第一个Member的name属性:" + name);
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(context, ieee, String.class);
System.out.println("取出ieee对象的第一个Member的第七个Inventions:" + invention);
}
/**
* 操作Map
*/
@Test
public void test4() {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Society ieee = new Society();
ieee.getOfficers().put("president", new Inventor("Tesla", "USA"));
ieee.getOfficers().put("advisors", new Inventor("BYD", "CN"));
ieee.getOfficers().put("secretary", new Inventor("BMW", "GE"));
ieee.getOfficers().put("treasurer", new Inventor("FLL", "FR"));
String name = parser.parseExpression("officers['advisors'].name").getValue(context, ieee, String.class);
System.out.println("取出ieee对象的advisors的name属性:" + name);
}
/**
* 内嵌List/Map
*/
@Test
public void test5() {
// 定义Parser,可以定义全局的parser
ExpressionParser parser = new SpelExpressionParser();
// [1, 2, 3, 4]
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue();
System.out.println(numbers);
// 嵌套: [[a, b], [x, y]]
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue();
System.out.println(listOfLists);
// {name=Nikola, dob=10-July-1856}
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue();
System.out.println(inventorInfo);
// 嵌套:{name={first=Nikola, last=Tesla}, dob={day=10, month=July, year=1856}}
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue();
System.out.println(mapOfMaps);
// List与Map可以嵌套使用,互相结合。
// 嵌套:[{name={first=Nikola, last=Tesla}}, {dob={day=10, month=July, year=1856}}]
List listOfMaps = (List) parser.parseExpression("{{name:{first:'Nikola',last:'Tesla'}},{dob:{day:10,month:'July',year:1856}}}").getValue();
System.out.println(listOfMaps);
}
/**
* 构建数组
*/
@Test
public void test6() {
ExpressionParser parser = new SpelExpressionParser();
int[] numbers1 = parser.parseExpression("new int[4]").getValue(int[].class);
// 数组并初始化
int[] numbers2 = parser.parseExpression("new int[]{1,2,3}").getValue(int[].class);
// 多维数组
int[][] numbers3 = parser.parseExpression("new int[4][5]").getValue(int[][].class);
}
/**
* 调用方法
*/
@Test
public void test7() {
ExpressionParser parser = new SpelExpressionParser();
// 调用substring方法
System.out.println(parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class));
// 调用societyContext中对象的isMember方法,并传值。
StandardEvaluationContext societyContext = new StandardEvaluationContext(new Society());
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);
}
/**
* 关系运算
* 每个符号操作符也可以被指定为纯字母的等价物。这避免了所使用的符号对于嵌入表达式的文档类型具有特殊含义的问题(例如在XML文档中)。所有文本操作符都不区分大小写。对应的文本是:
* lt (<)
* gt (>)
* le (<=)
* ge (>=)
* eq (==)
* ne (!=)
* div (/)
* mod (%)
* not (!)
*/
@Test
public void test8() {
ExpressionParser parser = new SpelExpressionParser();
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
boolean trueValue1 = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
}
/**
* instanceof 和 正则表达式的匹配操作符
* 使用基本类型时要小心,因为它们会立即被装箱为包装器类型,所以1 instanceof T(int)会计算为false,而1 instanceof T(Integer)会计算为true。
*/
@Test
public void test9() {
ExpressionParser parser = new SpelExpressionParser();
boolean value = parser.parseExpression("'xyz' instanceof T(Integer)").getValue(Boolean.class);
boolean trueValue = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
boolean falseValue = parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
}
/**
* 逻辑运算符:and, or, not
*/
@Test
public void test10() {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext societyContext = new StandardEvaluationContext(new Society());
boolean value = parser.parseExpression("true and false").getValue(Boolean.class);
String expression1 = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean value1 = parser.parseExpression(expression1).getValue(societyContext, Boolean.class);
boolean value2 = parser.parseExpression("true or false").getValue(Boolean.class);
String expression2 = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean valu3 = parser.parseExpression(expression2).getValue(societyContext, Boolean.class);
boolean value4 = parser.parseExpression("!true").getValue(Boolean.class);
String expression3 = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean value5 = parser.parseExpression(expression3).getValue(societyContext, Boolean.class);
}
/**
* 数学计算符
*/
@Test
public void test11() {
ExpressionParser parser = new SpelExpressionParser();
parser.parseExpression("1 + 1").getValue(Integer.class); // 2
parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string'
parser.parseExpression("1 - -3").getValue(Integer.class); // 4
parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
parser.parseExpression("6 / -3").getValue(Integer.class); // -2
parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
parser.parseExpression("7 % 4").getValue(Integer.class); // 3
parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
}
/**
* 类型获取符:T()
* T(Type):用于引用静态类型。
* T(Type).staticMethod():用于调用静态方法。
* T(Type).STATIC_FIELD:用于访问静态字段。
* instanceof T(Type):用于类型检查。
*/
@Test
public void test12() {
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);
}
/**
* 调用构造函数
*/
@Test
public void test13() {
System.out.println(Inventor.class.getName());
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext societyContext = new StandardEvaluationContext(new Society());
Inventor einstein = parser.parseExpression("new com.xuyuan.spring.spel.SpelExam.Inventor('Albert Einstein', 'German')").getValue(Inventor.class);
// 创建一个新的Inventor,并且添加到members的list中
parser.parseExpression("Members.add(com.xuyuan.spring.spel.SpelExam.Inventor('Albert Einstein', 'German'))").getValue(societyContext);
System.out.println(societyContext.getRootObject().getValue());
}
/**
* Spel变量
* #variableName语法在表达式中引用变量
*/
@Test
public void test14() {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("newName", "Mike Tesla"); // 设置变量
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
// 获取变量newName,并将其赋值给name属性
parser.parseExpression("Name = #newName").getValue(context, tesla);
System.out.println(tesla.getName()); // "Mike Tesla"
}
/**
* #this变量引用当前的评估对象(根据该评估对象解析非限定引用)。
* #root变量总是被定义并引用根上下文对象。虽然#this可能会随着表达式的组成部分的计算而变化,但是#root总是指根。
*/
@Test
public void test15() {
// 创建一个Integer数组
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2, 3, 5, 7, 11, 13, 17));
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("primes", primes);
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context);
System.out.println(primesGreaterThanTen);
}
/**
* 三元表达式
*/
@Test
public void test16() {
ExpressionParser parser = new SpelExpressionParser();
String expression = "10 > 5 ? 'ten is greater than five' : 'ten is not greater than five'";
parser.parseExpression(expression).getValue(String.class);
}
/**
* Elvis操作符
*/
@Test
public void test17() {
ExpressionParser parser = new SpelExpressionParser();
String name = "Elvis Presley";
String displayName = (name != null ? name : "Unknown");
String name1 = parser.parseExpression("name?:'Unknown'").getValue(String.class);
System.out.println(name1); // 'Unknown'
}
/**
* 安全导航运算符:obj?.prop
* 安全导航操作符用于避免NullPointerException,来自Groovy语言。
* 通常,当引用一个对象时,可能需要在访问该对象的方法或属性之前验证它不为null。为了避免这种情况,安全导航运算符返回null,而不是引发异常。以下示例显示了如何使用安全导航运算符
*/
@Test
public void test18() {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan", "Croatia"));
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city); // Smiljan
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city); // null - does not throw NullPointerException!!!
}
/**
* 集合选择:?[predicate]
*/
@Test
public void test19() {
Society ieee = new Society();
ieee.getMembers().add(new Inventor("Tesla", "USA"));
ieee.getMembers().add(new Inventor("BYD", "CN"));
ieee.getMembers().add(new Inventor("BMW", "GE"));
ieee.getMembers().add(new Inventor("FLL", "FR"));
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(ieee);
// 语法.?[selectionExpression]
List<Inventor> list = parser.parseExpression("Members.?[Nationality == 'Serbian']").getValue(context, List.class);
Map<String, Integer> map = new HashMap<>();
context.setVariable("map", map);
map.put("Tesla0", 28);
map.put("Tesla1", 26);
map.put("Tesla2", 25);
// 返回value小于27的值
// 使用参数化的 Map 类型
Map<String, Integer> newMap = parser.parseExpression("#map.?[value < 27]").getValue(context, Map.class);
System.out.println(newMap); // 输出符合条件的键值对
}
/**
* 集合投影:![expression]
*/
@Test
public void test20() {
Society ieee = new Society();
ieee.getMembers().add(new Inventor("Tesla", "USA"));
ieee.getMembers().add(new Inventor("BYD", "CN"));
ieee.getMembers().add(new Inventor("BMW", "GE"));
ieee.getMembers().add(new Inventor("FLL", "FR"));
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(ieee);
List placesOfBirth = parser.parseExpression("Members.![placeOfBirth?.city]").getValue(context, List.class);
}
/**
* 表达式模板:#{expression}
* 通常使用#{}作为模板,与字符串拼接
*/
@Test
public void test21() {
ExpressionParser parser = new SpelExpressionParser();
// 通常使用#{}作为模板,与字符串拼接起来
String randomPhrase = parser.parseExpression("random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue(String.class);
System.out.println(randomPhrase); // "random number is 0.7038186818312008"
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class PlaceOfBirth {
private String city;
private String country;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Society {
private String name;
public String Advisors = "advisors";
public String President = "president";
private List<Inventor> members = new ArrayList<>();
private Map officers = new HashMap();
public boolean isMember(String name) {
for (Inventor inventor : members) {
if (inventor.getName().equals(name)) {
return true;
}
}
return false;
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Inventor {
private String name;
private String nationality;
private String[] inventions;
private Date birthdate;
private PlaceOfBirth placeOfBirth;
public Inventor(String name, String nationality) {
GregorianCalendar c = new GregorianCalendar();
this.name = name;
this.nationality = nationality;
this.birthdate = c.getTime();
}
public Inventor(String name, Date birthdate, String nationality) {
this.name = name;
this.nationality = nationality;
this.birthdate = birthdate;
}
}
}