1. Overview
1.概述
The Spring Expression Language (SpEL) is a powerful expression language that supports querying and manipulating an object graph at runtime. We can use it with XML or annotation-based Spring configurations.
Spring表达式语言(SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。我们可以将其用于XML或基于注解的Spring配置。
There are several operators available in the language:
在语言中,有几个运算符可用。
Type | Operators |
---|---|
Arithmetic | +, -, *, /, %, ^, div, mod |
Relational | <, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge |
Logical | and, or, not, &&, ||, ! |
Conditional | ?: |
Regex | matches |
2. Operators
2.操作者
For these examples, we will use annotation-based configuration. Find more details about XML configuration in later sections of this article.
对于这些例子,我们将使用基于注解的配置。在本文后面的章节中可以找到更多关于XML配置的细节。
SpEL expressions begin with the # symbol and are wrapped in braces: #{expression}.
SpEL表达式以#符号开始,并被包裹在大括号中。#{expression}。
Properties can be referenced in a similar fashion, starting with a $ symbol and wrapped in braces: ${property.name}.
可以用类似的方式引用属性,以$符号开始,用大括号包裹。${property.name}。
Property placeholders cannot contain SpEL expressions, but expressions can contain property references:
属性占位符不能包含SpEL表达式,但表达式可以包含属性引用。
#{${someProperty} + 2}
In the example above, assume someProperty has value 2, so the resulting expression would be 2 + 2, which would be evaluated to 4.
在上面的例子中,假设someProperty的值为2,那么产生的表达式将是2+2,将被评估为4。
2.1. Arithmetic Operators
2.1.算术运算符
SpEL supports all basic arithmetic operators:
SpEL支持所有的基本算术运算符。
@Value("#{19 + 1}") // 20
private double add;
@Value("#{'String1 ' + 'string2'}") // "String1 string2"
private String addString;
@Value("#{20 - 1}") // 19
private double subtract;
@Value("#{10 * 2}") // 20
private double multiply;
@Value("#{36 / 2}") // 19
private double divide;
@Value("#{36 div 2}") // 18, the same as for / operator
private double divideAlphabetic;
@Value("#{37 % 10}") // 7
private double modulo;
@Value("#{37 mod 10}") // 7, the same as for % operator
private double moduloAlphabetic;
@Value("#{2 ^ 9}") // 512
private double powerOf;
@Value("#{(2 + 2) * 2 + 9}") // 17
private double brackets;
Divide and modulo operations have alphabetic aliases, div for / and mod for %. The + operator can also be used to concatenate strings.
Divide和modulo操作有字母别名,div表示/,mod表示%。+操作符也可用于串联字符串。
2.2. Relational and Logical Operators
2.2.关系型和逻辑型操作符
SpEL also supports all basic relational and logical operations:
SpEL还支持所有基本的关系和逻辑操作。
@Value("#{1 == 1}") // true
private boolean equal;
@Value("#{1 eq 1}") // true
private boolean equalAlphabetic;
@Value("#{1 != 1}") // false
private boolean notEqual;
@Value("#{1 ne 1}") // false
private boolean notEqualAlphabetic;
@Value("#{1 < 1}") // false
private boolean lessThan;
@Value("#{1 lt 1}") // false
private boolean lessThanAlphabetic;
@Value("#{1 <= 1}") // true
private boolean lessThanOrEqual;
@Value("#{1 le 1}") // true
private boolean lessThanOrEqualAlphabetic;
@Value("#{1 > 1}") // false
private boolean greaterThan;
@Value("#{1 gt 1}") // false
private boolean greaterThanAlphabetic;
@Value("#{1 >= 1}") // true
private boolean greaterThanOrEqual;
@Value("#{1 ge 1}") // true
private boolean greaterThanOrEqualAlphabetic;
All relational operators have alphabetic aliases as well. For example, in XML-based configs we can’t use operators containing angle brackets (<, <=, >, >=). Instead, we can use lt (less than), le (less than or equal), gt (greater than) or ge (greater than or equal).
所有的关系运算符也都有字母别名。例如,在基于XML的配置中,我们不能使用含有角括号的运算符(<, <=, >, >=)。相反,我们可以使用lt(小于)、le(小于或等于)、gt(大于)或ge(大于或等于)。
2.3. Logical Operators
2.3.逻辑运算符
SpEL also supports all basic logical operations:
SpEL还支持所有基本的逻辑操作。
@Value("#{250 > 200 && 200 < 4000}") // true
private boolean and;
@Value("#{250 > 200 and 200 < 4000}") // true
private boolean andAlphabetic;
@Value("#{400 > 300 || 150 < 100}") // true
private boolean or;
@Value("#{400 > 300 or 150 < 100}") // true
private boolean orAlphabetic;
@Value("#{!true}") // false
private boolean not;
@Value("#{not true}") // false
private boolean notAlphabetic;
As with arithmetic and relational operators, all logical operators also have alphabetic clones.
与算术和关系运算符一样,所有逻辑运算符也有字母克隆。
2.4. Conditional Operators
2.4.条件运算符
We use conditional operators for injecting different values depending on some condition:
我们使用条件运算符,根据一些条件注入不同的值。
@Value("#{2 > 1 ? 'a' : 'b'}") // "a"
private String ternary;
We use the ternary operator for performing compact if-then-else conditional logic inside the expression. In this example, we’re trying to check if there was true or not.
我们使用三元运算符在表达式内部执行紧凑的if-then-else条件逻辑。在这个例子中,我们要检查是否有true。
Another common use for the ternary operator is to check if some variable is null and then return the variable value or a default:
三元运算符的另一个常见用途是检查某些变量是否为null,然后返回变量值或默认值。
@Value("#{someBean.someProperty != null ? someBean.someProperty : 'default'}")
private String ternary;
The Elvis operator is a way of shortening of the ternary operator syntax for the case above used in the Groovy language. It is also available in SpEL.
Elvis运算符是对Groovy语言中使用的上述情况的三元运算符语法的一种缩短方式。它在SpEL中也是可用的。
This code is equivalent to the code above:
这个代码等同于上面的代码。
@Value("#{someBean.someProperty ?: 'default'}") // Will inject provided string if someProperty is null
private String elvis;
2.5. Using Regex in SpEL
2.5.在SpEL中使用Regex
We can use the matches operator to check whether or not a string matches a given regular expression:
我们可以使用 matches操作符来检查一个字符串是否匹配给定的正则表达式。
@Value("#{'100' matches '\\d+' }") // true
private boolean validNumericStringResult;
@Value("#{'100fghdjf' matches '\\d+' }") // false
private boolean invalidNumericStringResult;
@Value("#{'valid alphabetic string' matches '[a-zA-Z\\s]+' }") // true
private boolean validAlphabeticStringResult;
@Value("#{'invalid alphabetic string #$1' matches '[a-zA-Z\\s]+' }") // false
private boolean invalidAlphabeticStringResult;
@Value("#{someBean.someValue matches '\d+'}") // true if someValue contains only digits
private boolean validNumericValue;
2.6. Accessing List and Map Objects
2.6.访问List和Map对象
With the help of SpEL, we can access the contents of any Map or List in the context.
在SpEL的帮助下,我们可以访问上下文中任何Map或List的内容。
We’ll create new bean workersHolder that will store information about some workers and their salaries in a List and a Map:
我们将创建新的Bean workersHolder,它将在List和Map中存储一些工人和他们的工资信息。
@Component("workersHolder")
public class WorkersHolder {
private List<String> workers = new LinkedList<>();
private Map<String, Integer> salaryByWorkers = new HashMap<>();
public WorkersHolder() {
workers.add("John");
workers.add("Susie");
workers.add("Alex");
workers.add("George");
salaryByWorkers.put("John", 35000);
salaryByWorkers.put("Susie", 47000);
salaryByWorkers.put("Alex", 12000);
salaryByWorkers.put("George", 14000);
}
//Getters and setters
}
Now we can access the values of the collections using SpEL:
现在我们可以使用SpEL访问集合的值。
@Value("#{workersHolder.salaryByWorkers['John']}") // 35000
private Integer johnSalary;
@Value("#{workersHolder.salaryByWorkers['George']}") // 14000
private Integer georgeSalary;
@Value("#{workersHolder.salaryByWorkers['Susie']}") // 47000
private Integer susieSalary;
@Value("#{workersHolder.workers[0]}") // John
private String firstWorker;
@Value("#{workersHolder.workers[3]}") // George
private String lastWorker;
@Value("#{workersHolder.workers.size()}") // 4
private Integer numberOfWorkers;
3. Use in Spring Configuration
3.在Spring配置中使用
3.1. Referencing a Bean
3.1.引用一个Bean
In this example, we’ll look at how to use SpEL in XML-based configuration. We can use expressions to reference beans or bean fields/methods.
在这个例子中,我们将看看如何在基于XML的配置中使用SpEL。我们可以使用表达式来引用Bean或Bean字段/方法。
For example, suppose we have the following classes:
例如,假设我们有以下类别。
public class Engine {
private int capacity;
private int horsePower;
private int numberOfCylinders;
// Getters and setters
}
public class Car {
private String make;
private int model;
private Engine engine;
private int horsePower;
// Getters and setters
}
Now we create an application context in which expressions are used to inject values:
现在我们创建一个应用程序上下文,其中表达式被用来注入值。
<bean id="engine" class="com.baeldung.spring.spel.Engine">
<property name="capacity" value="3200"/>
<property name="horsePower" value="250"/>
<property name="numberOfCylinders" value="6"/>
</bean>
<bean id="someCar" class="com.baeldung.spring.spel.Car">
<property name="make" value="Some make"/>
<property name="model" value="Some model"/>
<property name="engine" value="#{engine}"/>
<property name="horsePower" value="#{engine.horsePower}"/>
</bean>
Take a look at the someCar bean. The engine and horsePower fields of someCar use expressions that are bean references to the engine bean and horsePower field respectively.
看一下someCar Bean。someCar的engine和horsePower字段使用的表达式分别是对enginebean和horsePower字段的bean引用。
To do the same with annotation-based configurations, use the @Value(“#{expression}”) annotation.
要对基于注解的配置进行同样的操作,请使用@Value(“#{expression}”) 注解。
3.2. Using Operators in Configuration
3.2.在配置中使用运算符
Each operator from the first section of this article can be used in XML and annotation-based configurations.
本文第一节中的每个运算符都可以在基于XML和注释的配置中使用。
However, remember that in XML-based configuration, we can’t use the angle bracket operator “<“. Instead, we should use the alphabetic aliases, such as lt (less than) or le (less than or equals).
然而,请记住,在基于XML的配置中,我们不能使用角括号操作符”<“。相反,我们应该使用字母别名,如lt(小于)或le(小于或等于)。
For annotation-based configurations, there are no such restrictions:
对于基于注释的配置,没有这种限制。
public class SpelOperators {
private boolean equal;
private boolean notEqual;
private boolean greaterThanOrEqual;
private boolean and;
private boolean or;
private String addString;
// Getters and setters
@Override
public String toString() {
// toString which include all fields
}
Now we will add a spelOperators bean to the application context:
现在我们将在应用程序上下文中添加一个spelOperatorsbean。
<bean id="spelOperators" class="com.baeldung.spring.spel.SpelOperators">
<property name="equal" value="#{1 == 1}"/>
<property name="notEqual" value="#{1 lt 1}"/>
<property name="greaterThanOrEqual" value="#{someCar.engine.numberOfCylinders >= 6}"/>
<property name="and" value="#{someCar.horsePower == 250 and someCar.engine.capacity lt 4000}"/>
<property name="or" value="#{someCar.horsePower > 300 or someCar.engine.capacity > 3000}"/>
<property name="addString" value="#{someCar.model + ' manufactured by ' + someCar.make}"/>
</bean>
Retrieving that bean from the context, we can then verify that values were injected properly:
从上下文中检索该Bean,我们就可以验证值是否被正确注入了。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
SpelOperators spelOperators = (SpelOperators) context.getBean("spelOperators");
Here we can see the output of the toString method of spelOperators bean:
这里我们可以看到toString方法对spelOperators bean的输出。
[equal=true, notEqual=false, greaterThanOrEqual=true, and=true,
or=true, addString=Some model manufactured by Some make]
4. Parsing Expressions Programmatically
4.以编程方式解析表达式
At times, we may want to parse expressions outside the context of configuration. Fortunately, this is possible using SpelExpressionParser.
有时,我们可能想在配置的背景之外解析表达式。幸运的是,使用SpelExpressionParser可以做到这一点。
We can use all operators that we saw in previous examples but should use them without braces and hash symbol. That is, if we want to use an expression with the + operator when used in Spring configuration, the syntax is #{1 + 1}; when used outside of configuration, the syntax is simply 1 + 1.
我们可以使用在之前的例子中看到的所有运算符,但在使用时应该去掉大括号和哈希符号。也就是说,如果我们想在Spring配置中使用带有+ 运算符的表达式,其语法是#{1 + 1};在配置之外使用时,其语法只是1 + 1。
In the following examples, we will use the Car and Engine beans defined in the previous section.
在下面的例子中,我们将使用上一节中定义的Car和Engine Bean。
4.1. Using ExpressionParser
4.1.使用ExpressionParser
Let’s look at a simple example:
我们来看看一个简单的例子。
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("'Any string'");
String result = (String) expression.getValue();
ExpressionParser is responsible for parsing expression strings. In this example, SpEL parser will simply evaluate the string ‘Any String’ as an expression. Unsurprisingly, the result will be ‘Any String’.
ExpressionParser负责解析表达式字符串。在这个例子中,SpEL解析器将简单地把字符串‘Any String’作为一个表达式来评估。不出所料,结果将是‘Any String’。
As with using SpEL in configuration, we can use it to call methods, access properties or call constructors:
与在配置中使用SpEL一样,我们可以用它来调用方法、访问属性或调用构造函数。
Expression expression = expressionParser.parseExpression("'Any string'.length()");
Integer result = (Integer) expression.getValue();
Additionally, instead of directly operating on the literal, we could call the constructor:
此外,我们可以调用构造函数,而不是直接对字面进行操作。
Expression expression = expressionParser.parseExpression("new String('Any string').length()");
We can also access the bytes property of String class, in the same way, resulting in the byte[] representation of the string:
我们也可以用同样的方法访问String类的bytes属性,从而得到字符串的byte[]表示。
Expression expression = expressionParser.parseExpression("'Any string'.bytes");
byte[] result = (byte[]) expression.getValue();
We can chain method calls, just as in normal Java code:
我们可以像在正常的Java代码中一样,对方法进行连锁调用。
Expression expression = expressionParser.parseExpression("'Any string'.replace(\" \", \"\").length()");
Integer result = (Integer) expression.getValue();
In this case, the result will be 9 because we have replaced whitespace with the empty string.
在这种情况下,结果将是9,因为我们已经用空字符串替换了空白处。
If we don’t want to cast the expression result, we can use the generic method T getValue(Class<T> desiredResultType), in which we can provide the desired type of class that we want to be returned.
如果我们不想铸造表达式的结果,我们可以使用通用方法T getValue(Class<T> desiredResultType),在这个方法中我们可以提供我们想要返回的类的类型。
Note that EvaluationException will be thrown if the returned value cannot be cast to desiredResultType:
请注意,如果返回值不能被投到desiredResultType,将抛出EvaluationException。
Integer result = expression.getValue(Integer.class);
The most common usage is to provide an expression string that is evaluated against a specific object instance:
最常见的用法是提供一个表达式字符串,针对一个特定的对象实例进行评估。
Car car = new Car();
car.setMake("Good manufacturer");
car.setModel("Model 3");
car.setYearOfProduction(2014);
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("model");
EvaluationContext context = new StandardEvaluationContext(car);
String result = (String) expression.getValue(context);
In this case, the result will be equal to the value of the model field of the car object, “Model 3“. The StandardEvaluationContext class specifies which object the expression will be evaluated against.
在这种情况下,结果将等于car对象的model字段的值,”Model 3“。StandardEvaluationContext类指定了表达式将针对哪个对象被评估。
It cannot be changed after the context object is created. StandardEvaluationContext is expensive to construct, and during repeated usage, it builds up cached state that enables subsequent expression evaluations to be performed more quickly. Because of caching, it is good practice to reuse StandardEvaluationContext where possible if the root object does not change.
在创建上下文对象之后,它不能被改变。StandardEvaluationContext的构建成本很高,在重复使用期间,它建立了缓存状态,使后续表达式的评估能够更快地执行。由于缓存的存在,如果根对象不发生变化,在可能的情况下重用StandardEvaluationContext是一个好的做法。
However, if the root object is changed repeatedly, we can use the mechanism shown in the example below:
然而,如果根对象被反复改变,我们可以使用下面例子中的机制。
Expression expression = expressionParser.parseExpression("model");
String result = (String) expression.getValue(car);
Here we call the getValue method with an argument that represents the object to which we want to apply a SpEL expression.
在这里,我们调用getValue方法,该方法的参数代表我们想要应用SpEL表达式的对象。
We can also use the generic getValue method, just as before:
我们也可以使用通用的getValue方法,就像以前一样。
Expression expression = expressionParser.parseExpression("yearOfProduction > 2005");
boolean result = expression.getValue(car, Boolean.class);
4.2. Using ExpressionParser to Set a Value
4.2.使用ExpressionParser来设置一个值
Using the setValue method on the Expression object returned by parsing an expression, we can set values on objects. SpEL will take care of type conversion. By default, SpEL uses org.springframework.core.convert.ConversionService. We can create our own custom converter between types. ConversionService is generics aware, so we can use it with generics.
使用解析表达式返回的Expression对象上的setValue方法,我们可以对对象设置值。SpEL将负责类型转换。默认情况下,SpEL使用org.springframework.core.convert.ConversionService。我们可以在类型之间创建我们自己的自定义转换器。ConversionService是泛型感知的,所以我们可以用泛型来使用它。
Let’s take a look how we do that in practice:
让我们来看看我们在实践中是如何做到这一点的。
Car car = new Car();
car.setMake("Good manufacturer");
car.setModel("Model 3");
car.setYearOfProduction(2014);
CarPark carPark = new CarPark();
carPark.getCars().add(car);
StandardEvaluationContext context = new StandardEvaluationContext(carPark);
ExpressionParser expressionParser = new SpelExpressionParser();
expressionParser.parseExpression("cars[0].model").setValue(context, "Other model");
The resulting car object will have model “Other model“, which was changed from “Model 3“.
产生的汽车对象将有model“Other model“,从”Model 3“被改变。
4.3. Parser Configuration
4.3.解析器配置
In the following example, we will use this class:
在下面的例子中,我们将使用这个类。
public class CarPark {
private List<Car> cars = new ArrayList<>();
// Getter and setter
}
It is possible to configure ExpressionParser by calling the constructor with a SpelParserConfiguration object.
通过调用带有SpelParserConfiguration对象的构造函数,可以配置ExpressionParser。
For example, if we try to add car object into the cars array of CarPark class without configuring the parser, we will get an error like this:
例如,如果我们试图将car对象添加到CarPark类的cars数组中而不配置解析器,我们会得到这样的错误。
EL1025E:(pos 4): The collection has '0' elements, index '0' is invalid
We can change the behavior of the parser, to allow it to automatically create elements if the specified index is null (autoGrowNullReferences, the first parameter to the constructor), or to automatically grow an array or list to accommodate elements beyond its initial size (autoGrowCollections, the second parameter):
我们可以改变解析器的行为,让它在指定的索引为空时自动创建元素(autoGrowNullReferences,构造函数的第一个参数),或者自动增长数组或列表以容纳超出其初始大小的元素(autoGrowCollections,第二个参数)。
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
StandardEvaluationContext context = new StandardEvaluationContext(carPark);
ExpressionParser expressionParser = new SpelExpressionParser(config);
expressionParser.parseExpression("cars[0]").setValue(context, car);
Car result = carPark.getCars().get(0);
The resulting car object will be equal to the car object that was set as the first element of the cars array of carPark object from the previous example.
产生的car对象将等于car对象,该对象被设置为前面例子中carPark对象的cars数组的第一个元素。
5. Conclusion
5.结论
SpEL is a powerful, well-supported expression language that we can use across all the products in the Spring portfolio. We can use it to configure Spring applications or to write parsers to perform more general tasks in any application.
SpEL是一种功能强大、支持良好的表达式语言,我们可以在Spring组合的所有产品中使用。我们可以用它来配置Spring应用程序,也可以用它来编写解析器,以便在任何应用程序中执行更普遍的任务。
The code samples in this article are available in the linked GitHub repository.
本文中的代码样本可在链接的GitHub资源库中找到。