Spring-SpEL表达式
Spring Expression Language(SpEL) SpEL表达式
4 Spring Expression Language (SpEL)
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions
Spring Expression Language Guide
https://www.baeldung.com/spring-expression-language
Spring 从 3.x 开始支持 EL 表达式, Spring Expression Language(SpEL) 是类似于 OGNL 和 JSP EL 的表达式语言, 能够在运行时构建复杂表达式, 存取对象属性、调用对象方法等, 而且所有的 SpEL 都支持 XML 和 Annotation 两种方式, 使用的格式均为: #{SpEL expression}
, 例如 @Value("#{'user'.toUpperCase()}")
SpEL表达式求值
4.1 Evaluation
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-evaluation
通用SpEL表达式求值流程
1、构造 SpelExpressionParser
SpEL 解析器
2、调用解析器的 parseExpression()
方法解析表达式
3、调用表达式的 getValue()
方法获取结果,返回是 Object 类型需要强制类型转换,或者调用 getValue(@Nullable Class<T> desiredResultType)
返回指定类型
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@Test
public void testSpEL() {
// 1、构造解析器
ExpressionParser parser = new SpelExpressionParser();
// 2、解析表达式
Expression expression = parser.parseExpression("'this is a str'");
// 3、获取结果,返回是 Object 类型
String strValue = (String) expression.getValue();
}
方法调用示例
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();
访问JavaBean属性示例
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes()'
Expression exp = parser.parseExpression("'Hello World'.bytes");
byte[] bytes = (byte[]) exp.getValue();
SpEL 还支持嵌套属性,通过点表达式来访问,例如 prop1.prop2.prop3
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length");
int length = (Integer) exp.getValue();
通过构造方法构造String
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");
String message = exp.getValue(String.class);
基于上下文根对象求值
// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
// The constructor arguments are name, birthday, and nationality.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(tesla);
// name == "Nikola Tesla"
exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(tesla, Boolean.class);
// result == true
其中求值调用了 Object getValue(@Nullable Object rootObject) throws EvaluationException;
方法,表达式都是基于 rootObject 的。
EvaluationContext 求值上下文
变量可以通过 StandardEvaluationContext 的 setVariable() 方法设置到上下文中, 然后再 SpEL 表达式中通过 #变量名
使用变量;
// 定义变量
String name = "Tom";
EvaluationContext context = new StandardEvaluationContext(); // 表达式的上下文,
context.setVariable("myName", name); // 为了让表达式可以访问该对象, 先把对象放到上下文中
ExpressionParser parser = new SpelExpressionParser();
// 访问变量
parser.parseExpression("#myName").getValue(context, String.class); // Tom , 使用变量
其中使用了 Object getValue(EvaluationContext context, @Nullable Object rootObject) throws EvaluationException;
求值方法,从上下文中读。
表达式
文本表达式
文本表达式(Literal Expressions) 支持字符串、数字(int, real, hex)、boolean 和 null.
字符串使用单引号分隔。单引号本身在字符串中使用两个单引号字符表示
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();
逻辑运算(布尔运算)
逻辑与 and
&&
逻辑或 or
||
逻辑非 not
!
// 逻辑运算
System.out.println(parser.parseExpression("true and false").getValue(Boolean.class));
String exp = "!({'abc','efg','xyz'}.contains('abc')) && ('red' == 'red' && 83.2 > 80) || ('man' == 'man')";
System.out.println(parser.parseExpression(exp).getValue(Boolean.class)); // false
集合筛选(#this
)
变量 #this
表示当前操作的对象,常用于集合的过滤
// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
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);
集合包含判断
// 集合是否包含key - 表达式内inline构造集合
System.out.println(
parser.parseExpression("{'abc','efg','xyz'}.contains('abc')").getValue(Boolean.class)); // true
System.out.println(
parser.parseExpression("!{'abc','efg','xyz'}.contains('abc')").getValue(Boolean.class)); // false
// 集合是否包含key - 将集合变量放入上下文,再从上下文中获取集合变量
context.setVariable("set", ImmutableSet.of("red", "green", "blue"));
System.out.println(parser.parseExpression("#set.contains('red')").getValue(context, Boolean.class)); // true
System.out.println(parser.parseExpression("!#set.contains('red')").getValue(context, Boolean.class)); // false
集合元素访问
// 集合元素访问
List<String> strList = Lists.newArrayList("aaa", "bbb", "ccc");
Map<String, Object> myMap = ImmutableMap.of(
"strAttr", "strAttrValue",
"intAttr", 1222,
"doubleAttr", 92.3,
"listAttr", strList,
"mapAttr", ImmutableMap.of("k1", "v1")
);
context.setVariable("strList", strList);
context.setVariable("myMap", myMap);
System.out.println(parser.parseExpression("#strList[0]").getValue(context)); // aaa
System.out.println(parser.parseExpression("#myMap['intAttr']").getValue(context)); // 1222
SpEL 在 Java 对象上匹配求值
public class SpELUtils {
private static final ExpressionParser PARSER = new SpelExpressionParser();
/**
* SpEL 表达式匹配求值
* @param object 要匹配的对象
* @param condition 表达式
* @return
*/
public static boolean spelObjectMatch(Object object, String condition) {
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("object", object);
try {
return Boolean.TRUE.equals(PARSER.parseExpression(condition).getValue(context, Boolean.class));
} catch (Exception e) {
System.out.println("SpEL match error " + e.getMessage());
return false;
}
}
}
@Test
public void testObjectMatch() {
List<Book> books = Lists.newArrayList(
new Book("语文书", 9.2),
new Book("数学书", 10D));
Student student = Student.builder()
.name("小明")
.score(93.4)
.books(books)
.attributes(ImmutableMap.of(
"strAttr", "strAttrValue",
"intAttr", 1222,
"doubleAttr", 92.3,
"listAttr", books,
"mapAttr", ImmutableMap.of("k1", "v1")
))
.build();
Assertions.assertTrue(SpELUtils.spelObjectMatch(student, "#object.name == '小明'"));
Assertions.assertTrue(SpELUtils.spelObjectMatch(student, "#object.score >= 90"));
// 从 book 数组中筛选出 name=语文书 的书比较其 price 值(注意使用 #this.name=='语文书' 做集合筛选后的结果还是割集合,所以后面取元素要加[0])
Assertions.assertTrue(SpELUtils.spelObjectMatch(student, "#object.books.?[#this.name=='语文书'][0].price == 9.2"));
// 从 attributes 的 mapAttr 中获取 k1 的值并比较
Assertions.assertTrue(SpELUtils.spelObjectMatch(student, "#object.attributes['mapAttr']['k1'] == 'v1'"));
}
@Data
@Builder
private static class Student {
private String name;
private Double score;
private List<Book> books;
private Map<String, Object> attributes;
}
@Data
@Builder
@AllArgsConstructor
private static class Book {
private String name;
private Double price;
}
问题
Property or field ‘xx’ cannot be found on null
SpelEvaluationException: EL1007E: Property or field ‘xx’ cannot be found on null
引用前加上 @
@Document(collection = "#{@cname.collectionName}")
SpelEvaluationException: EL1007E: Property or field ‘cname’ cannot be found on null #15707
https://github.com/spring-projects/spring-boot/issues/15707
上一篇 达梦数据库
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: