转自:https://www.baeldung.com/spring-expression-language
概述 Spring Expression Language(SpEL)是一种功能强大的表达语言,它支持在运行时查询和操作对象图。 它可以与XML或基于注释的Spring配置一起使用。
有几种可用的语言操作符:
类型
操作符
算数
+, -, * , /, %, ^, div, mod
关系
<, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge
逻辑
and, or, not, &&, ||, !
条件
?:
正则
matches
2.操作符 对于这些示例,我们将使用基于注释的配置。 有关XML配置的更多详细信息,可以在本文的后续部分中找到。
SpEL表达式以#
符号开头,并用大括号括起来:#{expression}
。 可以类似的方式引用属性,从$
符号开始,并用大括号括起来:$ {[property.name](http://property.name/)}
。 属性占位符不能包含SpEL表达式,但表达式可以包含属性引用:
在上面的示例中,假定someProperty的值为2,因此结果表达式将为2 + 2,其计算结果为4。
2.1. 算术运算符 支持所有基本算术运算符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @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;
除法和模运算具有字母别名,div
代表/
,mod
代表%
。 +
运算符还可用于连接字符串。
2.2. 关系和逻辑运算符 还支持所有基本的关系和逻辑操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @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;
所有关系运算符也都具有字母别名。 例如,在基于XML的配置中,我们不能使用包含尖括号(<
,<=
,>
,>=
)的运算符。 相反,我们可以使用lt
(小于),le
(小于或等于),gt
(大于)或ge
(大于或等于)。
2.3. 逻辑运算符 SpEL支持所有基本逻辑运算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @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;
与算术和关系运算符一样,所有逻辑运算符也具有字母别名。
2.4. 条件运算符 条件运算符用于根据某些条件注入不同的值:
1 2 @Value("#{2 > 1 ? 'a' : 'b'}" ) // "a" private String ternary;
三元运算符用于在表达式内部执行紧凑的if-then-else条件逻辑。 在此示例中,我们尝试检查是否为真。
三元运算符的另一个常见用法是检查某个变量是否为null,然后返回变量值或默认值:
1 2 @Value("#{someBean.someProperty != null ? someBean.someProperty : 'default'}" ) private String ternary;
Elvis
运算符是一种缩短Groovy语言中使用的上述情况的三元运算符语法的方法。 它也可以在SpEL中使用。 下面的代码与上面的代码等效:
1 2 @Value("#{someBean.someProperty ?: 'default'}" ) // 如果someProperty为空的话,注入提供的字符串 private String elvis;
2.5. 在SpEL中使用正则表达式 matchs
运算符可用于检查字符串是否与给定的正则表达式匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @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 访问List和Map对象 借助SpEL,我们可以在上下文中访问任何Map或List的内容。 我们将创建新的bean workerHolder,它将在List和Map中存储有关一些工人及其工资的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @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 }
现在我们可以使用SpEL访问集合的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @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. 以编程方式解析表达式 有时,我们可能想在配置上下文之外解析表达式。 幸运的是,使用SpelExpressionParser
可以做到这一点。 我们可以使用前面示例中看到的所有运算符,但应使用不带花括号和哈希符号的运算符。 也就是说,如果要在Spring配置中使用带+
运算符的表达式,则语法为#{1 + 1};
在配置之外使用时,语法仅为1 +1
。
在以下示例中,我们将使用以下类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Engine { private int capacity; private int horsePower; private int numberOfCylinders; } public class Car { private String make; private int model; private Engine engine; private int horsePower; }
3.1. 使用ExpressionParser 让我们看一个简单的例子:
1 2 3 ExpressionParser expressionParser = new SpelExpressionParser(); Expression expression = expressionParser.parseExpression("'Any string'" ); String result = (String) expression.getValue();
ExpressionParser负责解析表达式字符串。 在此示例中,SpEL解析器将简单地将字符串“ Any String”作为表达式进行求值。 毫不奇怪,结果将是“Any String”。
与在配置中使用SpEL一样,我们可以使用它来调用方法,访问属性或调用构造函数。
1 2 Expression expression = expressionParser.parseExpression("'Any string'.length()" ); Integer result = (Integer) expression.getValue();
另外,我们可以直接调用构造函数,而不是直接对文字进行运算:
1 Expression expression = expressionParser.parseExpression("new String('Any string').length()" )
我们还可以通过相同的方式访问String类的bytes属性,从而得到字符串的byte []表示形式:
1 2 Expression expression = expressionParser.parseExpression("'Any string'.bytes" ); byte [] result = (byte []) expression.getValue();
我们可以链接方法调用,就像普通的Java代码一样:
1 2 Expression expression = expressionParser.parseExpression("'Any string'.replace(\" \", \"\").length()" ); Integer result = (Integer) expression.getValue();
在这种情况下,结果将为9,因为我们已用空字符串替换了空格。 如果我们不希望转换表达式结果,则可以使用通用方法T getValue(Class <T> requiredResultType)
,在该方法中,我们可以提供想要返回的类的所需类型。 请注意,如果不能将返回值强制转换为期望的结果类型,则将引发EvaluationException:
1 Integer result = expression.getValue(Integer.class);
最常见的用法是提供一个针对特定对象实例评估的表达式字符串:
1 2 3 4 5 6 7 8 9 10 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);
在这种情况下,结果将等于汽车对象“Model 3”的模型字段的值。 StandardEvaluationContext
类指定将针对其评估表达式的对象。
创建上下文对象后,无法更改它。 StandardEvaluationContext
的构造成本很高,并且在重复使用期间,它会建立缓存状态,从而使后续的表达式评估能够更快地执行。 由于缓存,在根对象不更改的情况下尽可能重用StandardEvaluationContext
是一个好习惯。
但是,如果根对象反复更改,则可以使用以下示例中所示的机制:
1 2 Expression expression = expressionParser.parseExpression("model" ); String result = (String) expression.getValue(car);
在这里,我们调用带有参数的getValue
方法,该参数表示要向其应用SpEL表达式的对象。 我们也可以像以前一样使用通用的getValue
方法:
1 2 Expression expression = expressionParser.parseExpression("yearOfProduction > 2005" ); boolean result = expression.getValue(car, Boolean.class);
3.2. 使用ExpressionParser设置值 在通过解析表达式返回的Expression
对象上使用setValue
方法,可以在对象上设置值。 SpEL将负责类型转换。 默认情况下,SpEL使用org.springframework.core.convert.ConversionService
。 我们可以在类型之间创建自己的自定义转换器。 ConversionService
具有泛型意识,因此可以与泛型一起使用。 让我们看一下如何在实践中使用它:
1 2 3 4 5 6 7 8 9 10 11 12 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" );
生成的汽车对象将具有“Other model”模型,该模型已从“Model 3”更改为。
3.3. 解析器配置 在下面的示例中,我们将使用以下类:
1 2 3 4 5 public class CarPark { private List<Car> cars = new ArrayList<>(); }
可以通过使用SpelParserConfiguration
对象调用构造函数来配置ExpressionParser
。 例如,如果我们尝试在不配置解析器的情况下将car
对象添加到CarPark
类的cars
数组中,则会出现如下错误:
1 EL1025E:(pos 4 ): The collection has '0' elements, index '0' is invalid
我们可以更改解析器的行为,以允许解析器在指定索引为null
时自动创建元素(autoGrowNullReferences
,构造函数的第一个参数),或自动增长数组或列表以容纳超出其初始大小的元素(autoGrowCollections
,第二个参数)。
1 2 3 4 5 6 7 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 );
生成的car
对象将等于在上一个示例中被设置为carPark
对象的cars
数组的第一个元素的car
对象。
4.结论 SpEL是一种功能强大且得到良好支持的表达语言,可以在Spring产品组合中的所有产品中使用。 它可用于配置Spring应用程序或编写解析器以在任何应用程序中执行更多常规任务。
本文中的代码示例 [GITHUB ]。