首页 > 其他分享 >绕不开的Spring的EL表达式

绕不开的Spring的EL表达式

时间:2022-09-03 10:44:59浏览次数:89  
标签:EL use true Spring Value private boolean 绕不开 expression

https://www.baeldung.com/spring-expression-language

1. Overview

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.

There are several operators available in the language:

TypeOperators
Arithmetic +, -, *, /, %, ^, div, mod
Relational <, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge
Logical and, or, not, &&, ||, !
Conditional ?:
Regex matches

2. Operators

 

For these examples, we will use annotation-based configuration. Find more details about XML configuration in later sections of this article.

SpEL expressions begin with the # symbol and are wrapped in braces: #{expression}.

Properties can be referenced in a similar fashion, starting with a $ symbol and wrapped in braces: ${property.name}.

Property placeholders cannot contain SpEL expressions, but expressions can contain property references:

#{${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.

2.1. Arithmetic Operators 

 

SpEL supports all basic arithmetic operators:

@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.

2.2. Relational and Logical Operators 

 

SpEL also supports all basic relational and logical operations:

@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).

2.3. Logical Operators

 

SpEL also supports all basic logical operations:

@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 

 

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.

Another common use for the ternary operator is to check if some variable is null and then return the variable value or a default:

@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.

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

 

We can use the matches operator to check whether or not a string matches a given regular expression:

@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 

 

With the help of SpEL, we can access the contents of any Map or List in the context.

We'll create new bean workersHolder that will store information about some workers and their salaries in a List and a 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:

@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.1. Referencing a 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.

 

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.

To do the same with annotation-based configurations, use the @Value(“#{expression}”) annotation.

3.2. Using Operators in Configuration 

 

Each operator from the first section of this article can be used in XML and annotation-based configurations.

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

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:

<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:

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:

[equal=true, notEqual=false, greaterThanOrEqual=true, and=true, 
or=true, addString=Some model manufactured by Some make]

4. Parsing Expressions Programmatically 

 

At times, we may want to parse expressions outside the context of configuration. Fortunately, this is possible using 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.

In the following examples, we will use the Car and Engine beans defined in the previous section.

4.1. Using 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'.

As with using SpEL in configuration, we can use it to call methods, access properties or call constructors:

 
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:

Expression expression = expressionParser.parseExpression("'Any string'.bytes");
byte[] result = (byte[]) expression.getValue();

We can chain method calls, just as in normal Java code:

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.

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.

Note that EvaluationException will be thrown if the returned value cannot be cast to desiredResultType:

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.

 

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.

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.

We can also use the generic getValue method, just as before:

Expression expression = expressionParser.parseExpression("yearOfProduction > 2005");
boolean result = expression.getValue(car, Boolean.class);

4.2. Using ExpressionParser to Set a Value 

 

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.

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“.

4.3. Parser Configuration 

 

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.

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:

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

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.

5. Conclusion

 

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.

The code samples in this article are available in the linked GitHub repository.

标签:EL,use,true,Spring,Value,private,boolean,绕不开,expression
From: https://www.cnblogs.com/tekikesyo/p/16652123.html

相关文章

  • SpringTask
    1.定时任务概述在项目中开发定时任务应该一种比较常见的需求,在Java中开发定时任务主要有三种解决方案:一是使用JDK自带的Timer,二是使用第3三方组件Quartz,三是使用Spri......
  • springboot学习
    springboot学习官方文档:https://spring.io/projects/spring-boot1、简介1.1、什么是spirngboot?springboot在spring的基础之上,搭建起来的框架,能够帮助我们整合市面上......
  • springboot配置类@ConfigurationProperties报错Not registered via @EnableConfigurat
    添加一个@Component可以解决此问题,只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能。......
  • django-celery-beat 获取下一次执行时间
    前言因为业务需要获取下一次执行时间在前端展示,查阅百度,谷歌都没能找到实现方式。通过官方文档https://django-celery-beat.readthedocs.io/en/latest/reference/django-c......
  • Relaxed_LCMDCBD可以复活输出drup文件的代码
    Relaxed_LCMDCBD是近期主流求解器。其中本身具有导出proof证明UNSAT的相应代码。可以按照以下步骤激活相应代码,求解的同时输出相应drup格式的文件。复活输出drup文件输出......
  • springboot启动图标banner
    将springboot项目中的banner.txt文件替换成下面代码即可${AnsiColor.BRIGHT_YELLOW}┏━┓┏━┓┏┛......
  • SpringMVC笔记
    SpringMVC框架1、回顾MVC1.1、什么是MVCMVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范。是将业务逻辑、数据、显示分离的方法来组织代......
  • Electron学习(一)
    1.什么是Electron:Electron是使用HTML、JavaScript和CSS构建的跨平台桌面应用程序。2.特点及优势:web技术:基于Chromium、Node.js跨平台:Electron兼容Mac、Windows和L......
  • shell: list_executable_file - 列出可执行文件的名称(linux)
    shell:list_executable_file-列出可执行文件的名称(linux)    一、shell:list_executable_file 1#!/usr/bin/bash234#file_name=list_executabl......
  • 数据库设计工具Navicat Data Modeler使用方法(含设计案例)
    1NavicatDataModeler介绍1.1简介NavicatDataModeler3.1是一套能帮助你快速直观地构建精确模型的图形化工具,使各个层面的用户都能轻松地创建高品质的数据模型。它......