Spring Expression Language Guide

1. Überblick

Die Spring Expression Language (SpEL) ist eine leistungsstarke Ausdruckssprache, die die Abfrage und Bearbeitung eines Objektdiagramms zur Laufzeit unterstützt. Es kann mit XML- oder Anmerkungs-basierten Spring-Konfigurationen verwendet werden.

Es gibt mehrere Operatoren in der Sprache:

| ================================================== ======= | Typ | Operatoren | Arithmetik | , -, ** ,/,%, ^, div, mod | Relational | <,>, ==,! =, ⇐,> =, lt, gt, eq, ne, le, ge | logisch | und nicht

2. Betreiber

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

SpEL-Ausdrücke beginnen mit dem Symbol # und sind in geschweifte Klammern eingeschlossen:

#{Ausdruck} . Eigenschaften können auf ähnliche Weise referenziert werden, beginnend mit einem $ -Symbol und in geschweifte Klammern eingeschlossen: $ \ {property.name}

Platzhalter für Eigenschaften können keine SpEL-Ausdrücke enthalten, Ausdrücke können jedoch Eigenschaftenreferenzen enthalten:

#{${someProperty} + 2}

Nehmen Sie in dem obigen Beispiel an, dass someProperty den Wert 2 hat. Der resultierende Ausdruck wäre also 2 2, der mit 4 ausgewertet 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}")//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;

Alle relationalen Operatoren haben auch alphabetische Aliase. In XML-basierten Konfigurationen können wir beispielsweise keine Operatoren verwenden, die spitze Klammern enthalten ( < , ⇐, > , > = ). 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("#{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;

Wie bei arithmetischen und relationalen Operatoren verfügen auch alle logischen Operatoren über alphabetische Klone.

** 2.4. Bedingte Operatoren

**

Bedingte Operatoren werden verwendet, um abhängig von bestimmten Bedingungen verschiedene Werte einzufügen:

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

Der ternäre Operator wird für die Ausführung einer kompakten if-then-else-Bedingungslogik im Ausdruck verwendet. In diesem Beispiel versuchen wir zu prüfen, ob true vorhanden ist oder nicht.

Eine weitere häufige Anwendung für den ternären Operator ist die Überprüfung, ob eine Variable null ist, und dann den Variablenwert oder einen Standardwert zurückgeben:

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

Der Elvis-Operator ist eine Möglichkeit, die ternäre Operatorsyntax für den oben genannten Fall in der Groovy-Sprache zu verkürzen. Es ist auch in SpEL erhältlich. Der Code unten entspricht dem Code oben:

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

2.5. Verwendung von Regex in SpEL

Mit dem Operator matches 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. Zugriff auf List - und Map -Objekte

**

Mit Hilfe von SpEL können wir auf den Inhalt einer beliebigen Map oder List im Kontext zugreifen. Wir erstellen eine neue Bean workersHolder , die Informationen zu einigen Arbeitern und deren Gehältern in einer List und einer Map speichert:

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

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. Verwenden Sie in der Spring-Konfiguration

**

** 3.1. Eine Bean verweisen

**

In diesem Beispiel wird gezeigt, wie Sie SpEL in einer XML-basierten Konfiguration verwenden. 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:

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

Schauen Sie sich die someCar -Bean an. Die engine - und horsePower -Felder von someCar verwenden Ausdrücke, die Bean-Verweise auf die engine -Bean bzw. das horsePower -Feld sind.

Verwenden Sie für Annotations-basierte Konfigurationen dasselbe mit der Annotation @ Value (“# \ {expression}”)

** 3.2. Verwenden von Operatoren in der Konfiguration

**

Jeder Operator aus dem ersten Abschnitt dieses Artikels kann in XML- und annotationsbasierten Konfigurationen verwendet werden. Beachten Sie jedoch, dass wir in der XML-basierten Konfiguration den Winkelklammeroperator „<“ nicht verwenden können. Verwenden Sie stattdessen die alphabetischen Aliasnamen wie lt (kleiner als) oder le (kleiner als oder gleich). Für annotationsbasierte Konfigurationen gibt es keine derartigen Einschränkungen.

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
    }

Jetzt fügen wir dem Anwendungskontext eine spelOperators -Bean hinzu:

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

Wenn Sie diese Bean aus dem Kontext abrufen, können Sie überprüfen, ob die Werte ordnungsgemäß injiziert wurden:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
SpelOperators spelOperators = (SpelOperators) context.getBean("spelOperators");

Hier können wir die Ausgabe der toString -Methode von spelOperators bean sehen:

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

** 4. Ausdrücke programmgesteuert analysieren

**

Manchmal möchten wir Ausdrücke außerhalb des Konfigurationskontexts analysieren. Glücklicherweise ist dies mit SpelExpressionParser möglich. Wir können alle Operatoren verwenden, die wir in vorherigen Beispielen gesehen haben, sollten sie jedoch ohne Klammern und Hash-Zeichen verwenden. Wenn wir also in der Spring-Konfiguration einen Ausdruck mit dem Operator verwenden möchten, lautet die Syntax __ # \ {1 1};

In den folgenden Beispielen werden die im vorherigen Abschnitt definierten Beans Car und Engine verwendet.

** 4.1. ExpressionParser verwenden

**

Schauen wir uns ein einfaches Beispiel an:

ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("'Any string'");
String result = (String) expression.getValue();

ExpressionParser ist für das Analysieren von Ausdruckszeichenfolgen verantwortlich. In diesem Beispiel wertet der SpEL-Parser einfach die Zeichenfolge „Any String“ als Ausdruck aus. Es ist nicht überraschend, dass das Ergebnis „Any String“ __ ist.

Wie bei der Verwendung von SpEL in der Konfiguration können wir damit Methoden aufrufen, auf Eigenschaften zugreifen oder Konstruktoren aufrufen.

Expression expression = expressionParser.parseExpression("'Any string'.length()");
Integer result = (Integer) expression.getValue();

Anstatt direkt mit dem Literal zu arbeiten, können wir den Konstruktor auch aufrufen:

Expression expression = expressionParser.parseExpression("new String('Any string').length()");

Wir können auch auf die bytes -Eigenschaft der String -Klasse auf dieselbe Weise zugreifen, was zur Darstellung der Zeichenfolge byte[]führt:

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

Wir können Methodenaufrufe wie im normalen Java-Code verketten:

Expression expression = expressionParser.parseExpression("'Any string'.replace(\" \", \"\").length()");
Integer result = (Integer) expression.getValue();

In diesem Fall ist das Ergebnis 9, da Leerzeichen durch die leere Zeichenfolge ersetzt wurden. Wenn wir das Ausdrucksergebnis nicht umwandeln möchten, können Sie die generische Methode T getValue (Class <T> gewünschteResultType) verwenden, in der wir den gewünschten Klassentyp angeben können, den wir zurückgeben möchten. Beachten Sie, dass EvaluationException ausgelöst wird, wenn der zurückgegebene Wert nicht in desiredResultType umgewandelt werden kann:

Integer result = expression.getValue(Integer.class);

Am häufigsten wird eine Ausdruckszeichenfolge angegeben, die anhand einer bestimmten Objektinstanz ausgewertet wird:

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 diesem Fall ist das Ergebnis gleich dem Wert des Feldes model des car -Objekts „ Model 3 “. Die Klasse StandardEvaluationContext gibt an, für welches Objekt der Ausdruck ausgewertet wird.

Sie kann nicht geändert werden, nachdem das Kontextobjekt erstellt wurde.

StandardEvaluationContext ist teuer in der Konstruktion und baut während der wiederholten Verwendung einen zwischengespeicherten Zustand auf, der die spätere Auswertung von Ausdrücken ermöglicht. Aufgrund der Zwischenspeicherung empfiehlt es sich, StandardEvaluationContext soweit möglich wiederzuverwenden, wenn sich das Stammobjekt nicht ändert.

Wenn das Stammobjekt jedoch wiederholt geändert wird, können wir den im folgenden Beispiel gezeigten Mechanismus verwenden:

Expression expression = expressionParser.parseExpression("model");
String result = (String) expression.getValue(car);

Hier rufen wir die Methode getValue mit einem Argument auf, das das Objekt darstellt, auf das ein SpEL-Ausdruck angewendet werden soll. Wir können auch die generische getValue -Methode wie zuvor verwenden:

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

** 4.2. Verwenden von ExpressionParser zum Festlegen eines Werts

**

Mit der Methode setValue für das Objekt Expression , das durch Analysieren eines Ausdrucks zurückgegeben wird, können Werte für Objekte festgelegt werden. SpEL kümmert sich um die Typumwandlung. Standardmäßig verwendet SpEL org.springframework.core.convert.ConversionService . Wir können einen eigenen benutzerdefinierten Konverter zwischen den Typen erstellen. ConversionService ist genericsfähig und kann daher mit Generics verwendet werden. Schauen wir uns an, wie wir es in der Praxis einsetzen können:

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

Das resultierende Autoobjekt wird model " Other model " haben, das von " Model 3 " geändert wurde.

** 4.3. Parser-Konfiguration

**

Im folgenden Beispiel verwenden wir folgende Klasse:

public class CarPark {
    private List<Car> cars = new ArrayList<>();

   //Getter and setter
}

Es ist möglich, ExpressionParser zu konfigurieren, indem der Konstruktor mit einem SpelParserConfiguration -Objekt _. aufgerufen wird. Wenn wir beispielsweise versuchen, das car -Objekt in das cars -Array der CarPark_ -Klasse aufzunehmen, ohne den Parser zu konfigurieren, erhalten Sie eine Fehlermeldung wie diese

EL1025E:(pos 4): The collection has '0' elements, index '0' is invalid

Wir können das Verhalten des Parsers ändern, damit er automatisch Elemente erstellen kann, wenn der angegebene Index den Wert null hat ( autoGrowNullReferences, der erste Parameter für den Konstruktor), oder ein Array oder eine Liste automatisch vergrößert werden, um Elemente über ihre ursprüngliche Größe hinaus aufzunehmen ( autoGrowCollections , der zweite 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);

Das resultierende car -Objekt ist gleich dem car -Objekt, das als erstes Element des cars -Arrays des carPark -Objekts aus dem vorherigen Beispiel festgelegt wurde.

5. Fazit

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

Die Codebeispiele in diesem Artikel sind im https://github.com/eugenp/tutorials/tree/master/spring-spel [linked GitHub-Repository verfügbar.