SpEL表达式使用方法

1 SpEL简介

SpEL(Spring Expression Language)是一种用于在Spring框架中进行数据绑定和执行业务逻辑的表达式语言。Spring EL提供了一种简洁、灵活和强大的方式来访问对象的属性、调用方法、执行运算和逻辑判断等操作。

官方文档:https://docs.spring.io/spring-framework/reference/core/expressions.html

2 基本使用

2.1 通过@Value注解使用

可以将@Value注解加在字段,方法,方法参数,构造方法参数上使用,${…}表示直接读取上下文的属性值,而#{…}就表示使用SpEL进行运算。

字段示例:

public class FieldValueTestBean {

	@Value("#{systemProperties['user.region'] }")
	private String defaultLocale;

    private String defaultTimezone;

	public void setDefaultLocale(String defaultLocale) {
		this.defaultLocale = defaultLocale;
	}
}

方法示例:

public class PropertyValueTestBean {

	private String defaultLocale;

	@Value("#{ systemProperties['user.region'] }")
	public void setDefaultLocale(String defaultLocale) {
		this.defaultLocale = defaultLocale;
	}

	public String getDefaultLocale() {
		return this.defaultLocale;
	}
}

@Autowired方法示例:

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;
	}

	// ...
}

构造方法示例:

public class MovieRecommender {

	private String defaultLocale;

	private CustomerPreferenceDao customerPreferenceDao;

	public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
			@Value("#{systemProperties['user.country']}") String defaultLocale) {
		this.customerPreferenceDao = customerPreferenceDao;
		this.defaultLocale = defaultLocale;
	}

	// ...
}

2.2 编程方式执行SpEL

2.2.1 基本使用

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'"); 
String message = (String) exp.getValue(); // "Hello World"

2.2.2 设置根对象

Inventor tesla = new Inventor();
tesla.setName("Nikola Tesla");

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(tesla); // "Nikola Tesla"

2.2.3 设置上下文类型

以上的例子中,会创建一个上下文(EvaluationContext),默认实现类为StandardEvaluationContext
上下文接口有两种实现:

  • StandardEvaluationContext 完整的上下文功能
  • SimpleEvaluationContext 精简版的上下文,去除了Java类型参照、构造器、Bean参照等功能
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

Inventor tesla = new Inventor();
tesla.setName("Nikola Tesla");

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(context, tesla); // "Nikola Tesla"

2.2.4 SpEL配置

可通过SpelParserConfiguration的构造方法,设置SpEL相关的配置, 例如:

class Demo {
	public List<String> list;
}

// 开启空对象自动初始化,开启集合自动增长
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
ExpressionParser parser = new SpelExpressionParser(config);

ExpressionParser parser = new SpelExpressionParser(config);
Expression expression = parser.parseExpression("list[3]");
Demo demo = new Demo();
Object o = expression.getValue(demo); // 将不会报空指针异常,list会自动初始化, 并且将包含4个元素, 每个元素为空字符串

可以配置的功能有:

  • autoGrowNullReferences 开启自动生成对象,默认 false
  • autoGrowCollections 开启集合自动增长,默认 false
  • maximumAutoGrowSize 集合增长最大长度,默认 Integer.MAX_VALUE
  • maximumExpressionLength 表达式最大长度,默认 1000
  • compilerMode 开启预编译, 默认 OFF

2.2.4 编译SpEL

SpEL编译功能默认是关闭的,如果对性能有要求,那么可以开启预编译功能,

  • SpelCompilerMode.OFF 默认关闭
  • SpelCompilerMode.IMMEDIATE 立即编译,在第一次使用时就会编译
  • SpelCompilerMode.MIXED 混合编译,再使用几次后才会尝试编译,如果编译出错,就会用上次的编译成功的结果
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
		this.getClass().getClassLoader());

SpelExpressionParser parser = new SpelExpressionParser(config);
Expression expr = parser.parseExpression("payload");
MyMessage message = new MyMessage();
Object payload = expr.getValue(message);

3 表达式用法

3.1 字面量

  • String 使用单引号包裹
  • Number 使用int、long、float、double表示
  • Boolean true/false
  • Null null
xpressionParser parser = new SpelExpressionParser();

// evaluates to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

// evaluates to "Tony's Pizza"
String pizzaParlor = (String) parser.parseExpression("'Tony''s Pizza'").getValue();

double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evaluates to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();

3.2 属性、数组、列表、字典、索引

3.2.1 直接访问

通过对象.属性即可快速访问属性

// evaluates to 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);

String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);

属性访问对首字母大小写是不敏感的,所以上面的表达式也可以写成"Birthdate.Year + 1900",“PlaceOfBirth.City”。
另外可以通过getter来访问,比如 “getPlaceOfBirth().getCity()”

通过中括号加数字来访问数组、列表的指定的元素

xpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

// Inventions Array

// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
		context, tesla, String.class);

// Members List

// evaluates to "Nikola Tesla"
String name = parser.parseExpression("members[0].name").getValue(
		context, ieee, String.class);

// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
		context, ieee, String.class);

通过中括号加键名访问字典的元素

// Officer's Dictionary

Inventor pupin = parser.parseExpression("officers['president']").getValue(
		societyContext, Inventor.class);

// evaluates to "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
		societyContext, String.class);

// setting values
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
		societyContext, "Croatia");

3.2.2 集合、字典过滤

使用.?[选择表达式]可以方便的对数组、集合、字典进行过滤,类似于Java Stream的filter方法

List<Inventor> list = (List<Inventor>) parser.parseExpression(
		"members.?[nationality == 'Serbian']").getValue(societyContext);
		
Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);

.^[选择表达式]表示只取第一个,.$[选择表达式]表示只取最后一个

Inventor first = (Inventor) parser.parseExpression(
		"members.^[nationality == 'Serbian']").getValue(societyContext);
		
Inventor last= (Inventor) parser.parseExpression(
		"members.$[nationality == 'Serbian']").getValue(societyContext);

3.2.3 集合投影

使用.![投影表达式]可以方便的对数组、集合、字典进行转换成别的集合,类似于Java Stream的map方法

// evaluates to ["Smiljan", "Idvor"]
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
		.getValue(societyContext, List.class);

3.3 定义列表、字典、数组

定义列表

// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);

定义字典

// evaluates to a Java map containing the two entries
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);

Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);

定义数组

// 
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);

// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[] {1, 2, 3}").getValue(context);

// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);

初始化数组的表达式是无法预编译的

3.4 执行方法

和调用Java的方法一样,在SpEL中也是可以直接调用对象的方法的

// string literal, evaluates to "bc"
String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);

// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
		societyContext, Boolean.class);

3.5 运算符

3.5.1 关系运算

// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);

// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);

// evaluates to true
boolean trueValue = parser.parseExpression("'black' <= 'block'").getValue(Boolean.class); 

// evaluates to true
boolean falseValue = parser.parseExpression("'abc' > null").getValue(Boolean.class);

// uses CustomValue:::compareTo
boolean trueValue = parser.parseExpression("new CustomValue(2) > new CustomValue(1)").getValue(Boolean.class); 

除了用符号外,还可以用文本代替,比如在xml中使用时会发送标记的转义:

  • lt (<)
  • gt (>)
  • le (<=)
  • ge (>=)
  • eq (==)
  • ne (!=)

除了以上的关系运算,还支持between, instanceof, matches 关系运算

boolean result;

// evaluates to true
result = parser.parseExpression(
		"1 between {1, 5}").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"'elephant' between {'aardvark', 'zebra'}").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"123 instanceof T(Integer)").getValue(Boolean.class);

// evaluates to true
result = parser.parseExpression(
		"'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

3.5.2 逻辑运算

SpEL 支持一下逻辑运算

  • 与 (and, &&)
  • 或 (or , ||)
  • 非 ( not, !)
// -- AND --

// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- OR --

// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);

// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

// -- NOT --

// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);

// -- AND and NOT --

String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

3.5.3 字符串操作

+ 号表示字符串拼接,- 号表示单字符的ASCII码减去后的新值, * 号表示重复

// -- Concatenation --

// evaluates to "hello world"
String helloWorld = parser.parseExpression("'hello' + ' ' + 'world'")
		.getValue(String.class);

// -- Character Subtraction --

// evaluates to 'a'
char ch = parser.parseExpression("'d' - 3")
		.getValue(char.class);

// -- Repeat --

// evaluates to "abcabc"
String repeated = parser.parseExpression("'abc' * 2")
		.getValue(String.class);

3.5.4 数学运算

SpEL中支持以下数学运算

  • 加 (+)
  • 减(-)
  • 自增 (++)
  • 自减 (--)
  • 乘 (*)
  • 除 (/ , div)
  • 取模 (% , mod)
  • 指数幂 (^)
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

// -- Addition --

int two = parser.parseExpression("1 + 1").getValue(int.class);  // 2

// -- Subtraction --

int four = parser.parseExpression("1 - -3").getValue(int.class);  // 4

double d = parser.parseExpression("1000.00 - 1e4").getValue(double.class);  // -9000

// -- Increment --

// The counter property in Inventor has an initial value of 0.

// evaluates to 2; counter is now 1
two = parser.parseExpression("counter++ + 2").getValue(context, inventor, int.class);

// evaluates to 5; counter is now 2
int five = parser.parseExpression("3 + ++counter").getValue(context, inventor, int.class);

// -- Decrement --

// The counter property in Inventor has a value of 2.

// evaluates to 6; counter is now 1
int six = parser.parseExpression("counter-- + 4").getValue(context, inventor, int.class);

// evaluates to 5; counter is now 0
five = parser.parseExpression("5 + --counter").getValue(context, inventor, int.class);

// -- Multiplication --

six = parser.parseExpression("-2 * -3").getValue(int.class);  // 6

double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(double.class);  // 24.0

// -- Division --

int minusTwo = parser.parseExpression("6 / -3").getValue(int.class);  // -2

double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(double.class);  // 1.0

// -- Modulus --

int three = parser.parseExpression("7 % 4").getValue(int.class);  // 3

int oneInt = parser.parseExpression("8 / 5 % 2").getValue(int.class);  // 1

// -- Exponential power --

int maxInt = parser.parseExpression("(2^31) - 1").getValue(int.class);  // Integer.MAX_VALUE

int minInt = parser.parseExpression("-2^31").getValue(int.class);  // Integer.MIN_VALUE

// -- Operator precedence --

int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(int.class);  // -21

3.5.5 赋值运算

在SpEL中也可以通过=表达式或者setValue方法执行对某个变量赋值的操作

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

// 通过setValue方法赋值
parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic");

// 通过 = 在表达式中赋值
String aleks = parser.parseExpression(
		"name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);

3.5.6 三元运算

SpEL也支持 boolean ? trueValue : falseValue 形式的三元运算

String falseString = parser.parseExpression(
		"false ? 'trueExp' : 'falseExp'").getValue(String.class);

3.5.7 Elvis(埃尔维斯) 运算

埃尔维斯运算符是三元运算符语法的一种简化形式,以下示例中当name为null或者空字符串时,返回"Unknown"

ExpressionParser parser = new SpelExpressionParser();

String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class);
System.out.println(name);  // 'Unknown'

3.5.8 Safe Navigation(安全导航)

Safe Navigation Operator(安全导航操作符)?.是一种用于处理可能为 null 的对象引用的操作符。它可以在访问可能为 null 的对象成员时避免空指针异常。

// evaluates to null - does not throw NullPointerException
city = parser.parseExpression("placeOfBirth?.city") 
		.getValue(context, tesla, String.class);

也可用于数组、集合、字典的顾虑和投影

  • 安全过滤: ?.?[过滤表达式]
  • 安全首位: ?.\^[过滤表达式]
  • 安全末位: ?.$[过滤表达式]
  • 安全投影: ?.![投影表达式]

安全导航还可以连用

#person?.address?.city

3.5.9 运算符重载

默认情况下,在 SpEL 的操作枚举(加法、减法、除法、乘法、取模和幂)中定义的数学运算支持像数字这样的简单类型。通过提供一个OperatorOverloader的实现,表达式语言可以在其他类型上支持这些操作。
例如,如果我们想要重载加法运算符,以便使用+符号将两个列表连接起来,我们可以如下实现一个自定义的OperatorOverloader。

pubic class ListConcatenation implements OperatorOverloader {

	@Override
	public boolean overridesOperation(Operation operation, Object left, Object right) {
		return (operation == Operation.ADD &&
				left instanceof List && right instanceof List);
	}

	@Override
	public Object operate(Operation operation, Object left, Object right) {
		if (operation == Operation.ADD &&
				left instanceof List list1 && right instanceof List list2) {

			List result = new ArrayList(list1);
			result.addAll(list2);
			return result;
		}
		throw new UnsupportedOperationException(
			"No overload for operation %s and operands [%s] and [%s]"
				.formatted(operation, left, right));
	}
}

StandardEvaluationContext context = new StandardEvaluationContext();
context.setOperatorOverloader(new ListConcatenation());

// evaluates to a new list: [1, 2, 3, 4, 5]
parser.parseExpression("{1, 2, 3} + {2 + 2, 5}").getValue(context, List.class);

一个操作符重载器不会改变一个操作符的默认语义。例如,在上述例子中 2+2 仍然计算结果为 4。

3.6 类型

可以使用特殊的 T 操作符来指定一个Java类的实例。静态方法也通过使用这个操作符来调用。如果使用StandardEvaluationContext,那么 java.lang 包内的类型的 T()引用不需要是写完整的包名,但所有其他类型引用必须写完整。

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);

如果使用自定义EvaluationContext,则需要手动配置一个带有特定ClassLoader的StandardTypeLocator,以确保 SpEL 表达式解析器能够定位用户类型。
例如,spring-context模块中的StandardBeanExpressionResolver使用相应BeanFactory的 beanClassLoader来配置StandardTypeLocator。

3.7 对象构造

需要构造对象的时候,需要用 new 关键字并且要写完整的包名+类名 (除了java.lang包)

Inventor einstein = p.parseExpression(
		"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
		.getValue(Inventor.class);

3.8 变量

EvaluationContext 中使用setVariable()方法设置变量,在表达式中使用#变量名来获取变量的值, 变量名的命名规范遵循Java语言变量名的命名规范。

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");

EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("newName", "Mike Tesla");

parser.parseExpression("name = #newName").getValue(context, tesla);
System.out.println(tesla.getName());  // "Mike Tesla"

有特殊的两个变量 #this#root#this变量总是被定义并且指代当前正在评估的对象。#root变量总是被定义并且指代根上下文对象。

// Create a list of prime integers.
List<Integer> primes = List.of(2, 3, 5, 7, 11, 13, 17);

// Create parser and set variable 'primes' as the list of integers.
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("primes", primes);

// Select all prime numbers > 10 from the list (using selection ?{...}).
String expression = "#primes.?[#this > 10]";

// Evaluates to a list containing [11, 13, 17].
List<Integer> primesGreaterThanTen =
		parser.parseExpression(expression).getValue(context, List.class);

#this#root共用:

// Create parser and evaluation context.
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

// Create an inventor to use as the root context object.
Inventor tesla = new Inventor("Nikola Tesla");
tesla.setInventions("Telephone repeater", "Tesla coil transformer");

// Iterate over all inventions of the Inventor referenced as the #root
// object, and generate a list of strings whose contents take the form
// "<inventor's name> invented the <invention>." (using projection !{...}).
String expression = "#root.inventions.![#root.name + ' invented the ' + #this + '.']";

// Evaluates to a list containing:
// "Nikola Tesla invented the Telephone repeater."
// "Nikola Tesla invented the Tesla coil transformer."
List<String> results = parser.parseExpression(expression)
		.getValue(context, tesla, List.class);

3.9 方法

通过注册用户定义的函数来扩展 SpEL,这些函数可以在表达式中通过使用#functionName(…)语法来调用。函数可以通过setVariable()方法在EvaluationContext实现中作为变量进行注册。
StandardEvaluationContext还定义了registerFunction(…)方法,这些方法提供了一种方便的方式来将一个函数注册为java.lang.reflect.Methodjava.lang.invoke.MethodHandle

注册一个Mehtod:

public abstract class StringUtils {

	public static String reverseString(String input) {
		return new StringBuilder(input).reverse().toString();
	}
}

ExpressionParser parser = new SpelExpressionParser();

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",
		StringUtils.class.getMethod("reverseString", String.class));

// evaluates to "olleh"
String helloWorldReversed = parser.parseExpression(
		"#reverseString('hello')").getValue(context, String.class);

注册一个MethodHandle:

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
		MethodType.methodType(String.class, Object[].class));
context.setVariable("message", mh);

// evaluates to "Simple message: <Hello World>"
String message = parser.parseExpression("#message('Simple message: <%s>', 'Hello World', 'ignored')")
		.getValue(context, String.class);

如果目标和所有的参数都被绑定,这很可能会有更好的性能。在那种情况下,在 SpEL 表达式中不需要任何参数

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

String template = "This is a %s message with %s words: <%s>";
Object varargs = new Object[] { "prerecorded", 3, "Oh Hello World!", "ignored" };
MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
		MethodType.methodType(String.class, Object[].class))
		.bindTo(template)
		.bindTo(varargs); //here we have to provide arguments in a single array binding
context.setVariable("message", mh);

// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
String message = parser.parseExpression("#message()")
		.getValue(context, String.class);

3.10 Bean参照

如果上下文已经使用一个 Bean 解析器进行了配置,你可以通过使用@符号从表达式中查找 beans。

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);

要访问工厂 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);

4 模板表达式

模板表达式允许将字面文本与一个或多个求值块混合。每个求值块用你可以定义的前缀和后缀字符来界定。一个常见的选择是使用#{ }作为界定符。

String randomPhrase = parser.parseExpression(
		"random number is #{T(java.lang.Math).random()}",
		new TemplateParserContext()).getValue(String.class);

// evaluates to "random number is 0.7038186818312008"

TemplateParserContext ParserContext接口的实现类,定义了用#{ }包裹SpEL内容。

相关推荐

  1. SpEL表达式使用方法

    2024-06-06 16:36:06       8 阅读
  2. Spel 表达式

    2024-06-06 16:36:06       20 阅读
  3. SpEL 表达式是什么?

    2024-06-06 16:36:06       10 阅读
  4. SpEL使用

    2024-06-06 16:36:06       39 阅读
  5. Notepad++ 使用正则表达式删除空行空格方法

    2024-06-06 16:36:06       9 阅读
  6. [RK-Linux] RK3399使用开源TPL与SPL方式加载U-Boot

    2024-06-06 16:36:06       15 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-06 16:36:06       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-06 16:36:06       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-06 16:36:06       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-06 16:36:06       18 阅读

热门阅读

  1. 实例Python对比两个word文档并找出不同

    2024-06-06 16:36:06       9 阅读
  2. eazyexcel生成校验单元格内容的excel文件

    2024-06-06 16:36:06       10 阅读
  3. adam优化器计算过程(tensorflow)

    2024-06-06 16:36:06       9 阅读
  4. 网络数据库后端框架相关面试题

    2024-06-06 16:36:06       9 阅读
  5. MySQL数据库主从配置

    2024-06-06 16:36:06       7 阅读
  6. MySQL迁移达梦数据库避坑

    2024-06-06 16:36:06       8 阅读