Spring Expression Language Guide

1. Übersicht

Die Spring Expression Language (SpEL) ist eine leistungsstarke Ausdruckssprache, die das Abfragen und Bearbeiten eines Objektdiagramms zur Laufzeit unterstützt. Es kann mit XML- oder annotationsbasierten Spring-Konfigurationen verwendet werden.

In der Sprache stehen mehrere Operatoren zur Verfügung:

Art Betreiber
Arithmetik +, -, *, /,%, ^, div, mod
Relational , == ,! =, =, lt, gt, eq, ne, le, ge
Logisch und oder nicht &&, || ,!
Bedingt ?:
Regex Streichhölzer

2. Betreiber

Für diese Beispiele verwenden wir eine annotationsbasierte Konfiguration. Weitere Details zur XML-Konfiguration finden Sie in späteren Abschnitten dieses Artikels.

SpEL-Ausdrücke beginnen mit dem Symbol # und werden in geschweifte Klammern gesetzt: # {expression} . Eigenschaften können auf ähnliche Weise referenziert werden, beginnend mit einem $ -Symbol und in geschweiften Klammern: $ {property.name} . Eigenschaftsplatzhalter können keine SpEL-Ausdrücke enthalten, aber Ausdrücke können Eigenschaftsreferenzen enthalten:

#{${someProperty} + 2}

Nehmen Sie im obigen Beispiel an, dass someProperty den Wert 2 hat, sodass der resultierende Ausdruck 2 + 2 wäre, was mit 4 bewertet würde.

2.1. Rechenzeichen

Alle grundlegenden arithmetischen Operatoren werden unterstützt.

@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- und Modulo-Operationen haben alphabetische Aliase, div für / und mod für % . Der Operator + kann auch zum Verketten von Zeichenfolgen verwendet werden.

2.2. Relationale und logische Operatoren

Alle grundlegenden relationalen und logischen Operationen werden ebenfalls unterstützt.

@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}") // 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; 

Alle Vergleichsoperatoren haben auch alphabetische Aliase. In XML-basierten Konfigurationen können wir beispielsweise keine Operatoren mit spitzen Klammern ( < , <=, > , > = ) verwenden. Stattdessen können wir lt (kleiner als), le (kleiner als oder gleich), gt (größer als) oder ge (größer als oder gleich) verwenden.

2.3. Logische Operatoren

SpEL unterstützt alle grundlegenden logischen Operationen:

@Value("#") // true private boolean orAlphabetic; @Value("#{!true}") // false private boolean not; @Value("#{not true}") // false private boolean notAlphabetic;

Wie bei arithmetischen und relationalen Operatoren haben auch alle logischen Operatoren alphabetische Klone.

2.4. Bedingte Operatoren

Bedingte Operatoren werden verwendet, um abhängig von einer Bedingung unterschiedliche Werte zu injizieren:

@Value("#{2 > 1 ? 'a' : 'b'}") // "a" private String ternary;

Der ternäre Operator wird verwendet, um eine kompakte Wenn-Dann-Sonst-Bedingungslogik innerhalb des Ausdrucks auszuführen. In diesem Beispiel versuchen wir zu überprüfen, ob es wahr ist oder nicht.

Eine weitere häufige Verwendung für den ternären Operator besteht darin, zu überprüfen, ob eine Variable null ist, und dann den Variablenwert oder einen Standardwert zurückzugeben:

@Value("#{someBean.someProperty != null ? someBean.someProperty : 'default'}") private String ternary;

Der Elvis-Operator ist eine Möglichkeit, die Syntax des ternären Operators für den oben in der Groovy-Sprache verwendeten Fall zu verkürzen. Es ist auch in SpEL erhältlich. Der folgende Code entspricht dem obigen Code:

@Value("#{someBean.someProperty ?: 'default'}") // Will inject provided string if someProperty is null private String elvis;

2.5. Verwenden von Regex in SpEL

Mit dem Übereinstimmungsoperator kann überprüft werden, ob eine Zeichenfolge mit einem bestimmten regulären Ausdruck übereinstimmt.

@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. Zugreifenden Liste und Karte Objekte

Mit Hilfe von SpEL können wir auf den Inhalt jeder Karte oder Liste im Kontext zugreifen . Wir werden einen neuen Bean WorkerHolder erstellen , der Informationen über einige Arbeiter und ihre Gehälter in einer Liste und einer Karte speichert :

@Component("workersHolder") public class WorkersHolder { private List workers = new LinkedList(); private Map 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 }

Jetzt können wir mit SpEL auf die Werte der Sammlungen zugreifen:

@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. In Spring Konfiguration verwenden

3.1. Referenzieren einer Bohne

In diesem Beispiel wird erläutert, wie SpEL in einer XML-basierten Konfiguration verwendet wird. Ausdrücke können verwendet werden, um Beans oder Bean-Felder / -Methoden zu referenzieren. Angenommen, wir haben die folgenden Klassen:

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 }

Jetzt erstellen wir einen Anwendungskontext, in dem Ausdrücke zum Einfügen von Werten verwendet werden:

Schauen Sie sich die someCar- Bohne an. Der Motor und Pferdestärke Felder someCar Verwendung Ausdrücke , die Bohne Verweise auf denen sind Motor Bohne und Pferdestärke bzw. Feld.

Verwenden Sie die Annotation @Value ("# {expression}") , um dasselbe mit annotationsbasierten Konfigurationen zu tun .

3.2. Verwenden von Operatoren in der Konfiguration

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:

   = 6}"/>   300 or someCar.engine.capacity > 3000}"/>

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 wish to cast the expression result, we can use the generic method T getValue(Class 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 it 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 it can be used with generics. Let's take a look how we can use it 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 modelOther model” which was changed from “Model 3“.

4.3. Parser Configuration

In the following example, we will use the following class:

public class CarPark { private List 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 which was set as the first element of the cars array of carPark object from the previous example.

5. Conclusion

SpEL ist eine leistungsstarke, gut unterstützte Ausdruckssprache, die für alle Produkte im Spring-Portfolio verwendet werden kann. Es kann verwendet werden, um Spring-Anwendungen zu konfigurieren oder Parser zu schreiben, um allgemeinere Aufgaben in jeder Anwendung auszuführen.

Die Codebeispiele in diesem Artikel sind im verknüpften GitHub-Repository verfügbar.