Spring Expression Language Guide

1. Overzicht

De Spring Expression Language (SpEL) is een krachtige expressietaal die het opvragen en manipuleren van een objectgrafiek tijdens runtime ondersteunt. Het kan worden gebruikt met XML- of annotatie-gebaseerde Spring-configuraties.

Er zijn verschillende operators beschikbaar in de taal:

TypeOperatoren
Rekenkundig+, -, *, /,%, ^, div, mod
Relationeel, ==,! =, =, lt, gt, eq, ne, le, ge
Logischen, of, niet, &&, ||,!
Voorwaardelijk?:
Regexwedstrijden

2. Operatoren

Voor deze voorbeelden gebruiken we een op annotaties gebaseerde configuratie. Meer informatie over XML-configuratie vindt u in latere secties van dit artikel.

SpEL-uitdrukkingen beginnen met de # symbool, en zijn verpakt tussen accolades: #{uitdrukking}. Er kan op een vergelijkbare manier naar eigenschappen worden verwezen, te beginnen met een $ symbool, en verpakt tussen accolades: $ {property.name}. Tijdelijke aanduidingen voor eigenschappen kunnen geen SpEL-uitdrukkingen bevatten, maar uitdrukkingen kunnen verwijzingen naar eigenschappen bevatten:

# {$ {someProperty} + 2}

Ga in het bovenstaande voorbeeld uit van someProperty heeft waarde 2, dus de resulterende uitdrukking zou 2 + 2 zijn, wat zou worden geëvalueerd naar 4.

2.1. Rekenkundige operatoren

Alle elementaire rekenkundige operatoren worden ondersteund.

@Value ("# {19 + 1}") // 20 privé dubbele add; @Value ("# {'String1' + 'string2'}") // "String1 string2" private String addString; @Value ("# {20 - 1}") // 19 privé dubbel aftrekken; @Value ("# {10 * 2}") // 20 privé dubbele vermenigvuldiging; @Value ("# {36/2}") // 19 privé dubbele scheiding; @Value ("# {36 div 2}") // 18, hetzelfde als voor / operator private double divideAlphabetic; @Value ("# {37% 10}") // 7 privé dubbele modulo; @Value ("# {37 mod 10}") // 7, hetzelfde als voor% operator private double moduloAlphabetic; @Value ("# {2 ^ 9}") // 512 private dubbele powerOf; @Value ("# {(2 + 2) * 2 + 9}") // 17 persoonlijke dubbele haakjes; 

Verdeel- en modulo-bewerkingen hebben alfabetische aliassen, div voor / en mod voor %. De + operator kan ook worden gebruikt om strings samen te voegen.

2.2. Relationele en logische operators

Alle elementaire relationele en logische bewerkingen worden ook ondersteund.

@Value ("# {1 == 1}") // true private booleaanse gelijk; @Value ("# {1 eq 1}") // true private booleaanse 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 privé booleaanse waarde groter dan; @Value ("# {1 gt 1}") // false private boolean groterThanAlphabetic; @Value ("# {1> = 1}") // true private boolean groterThanOrEqual; @Value ("# {1 ge 1}") // true private booleaanse majorThanOrEqualAlphabetic; 

Alle relationele operatoren hebben ook alfabetische aliassen. In XML-gebaseerde configuraties kunnen we bijvoorbeeld geen operatoren gebruiken die punthaken bevatten (<, <=,>, >=). In plaats daarvan kunnen we gebruiken lt (minder dan), le (Minder dan of gelijk), gt (groter dan), of ge (groter dan of gelijk aan).

2.3. Logische operators

SpEL ondersteunt alle logische basisbewerkingen:

@Value ("#") // true private booleaanse orAlfabetisch; @Value ("# {! True}") // false private boolean niet; @Value ("# {not true}") // false private boolean notAlphabetic;

Net als bij rekenkundige en relationele operatoren hebben alle logische operatoren ook alfabetische klonen.

2.4. Voorwaardelijke operators

Voorwaardelijke operators worden gebruikt voor het injecteren van verschillende waarden, afhankelijk van een bepaalde voorwaarde:

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

De ternaire operator wordt gebruikt voor het uitvoeren van compacte als-dan-anders voorwaardelijke logica binnen de uitdrukking. In dit voorbeeld proberen we te controleren of er waar of niet.

Een ander veelgebruikt gebruik voor de ternaire operator is om te controleren of een variabele dat is nul en retourneer vervolgens de variabele waarde of een standaardwaarde:

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

De Elvis-operator is een manier om de ternaire operatorsyntaxis voor het bovenstaande geval in de Groovy-taal in te korten. Het is ook beschikbaar in SpEL. De onderstaande code is gelijk aan de bovenstaande code:

@Value ("# {someBean.someProperty?: 'Default'}") // Zal de opgegeven string injecteren als someProperty null is private String elvis;

2.5. Regex gebruiken in SpEL

De wedstrijden operator kan worden gebruikt om te controleren of een tekenreeks overeenkomt met een bepaalde reguliere expressie.

@Value ("# {'100' komt overeen met '\ d +'}") // true private boolean validNumericStringResult; @Value ("# {'100fghdjf' komt overeen met '\ d +'}") // false private boolean invalidNumericStringResult; @Value ("# {'geldige alfabetische tekenreeks' komt overeen met '[a-zA-Z \ s] +'}") // true private boolean validAlphabeticStringResult; @Value ("# {'ongeldige alfabetische tekenreeks # $ 1' komt overeen met '[a-zA-Z \ s] +'}") // false private boolean invalidAlphabeticStringResult; @Value ("# {someBean.someValue komt overeen met '\ d +'}") // waar als someValue alleen cijfers bevat private boolean validNumericValue;

2.6. Toegang verkrijgen Lijst en Kaart Voorwerpen

Met behulp van SpEL hebben we toegang tot de inhoud van elk Kaart of Lijst in de context. We zullen een nieuwe boon maken workersHolder waarin informatie over sommige werknemers en hun salarissen wordt opgeslagen in een Lijst en een Kaart:

@Component ("workersHolder") openbare klasse WorkersHolder {private List workers = nieuwe LinkedList (); privékaart salarisByWorkers = nieuwe HashMap (); openbare WorkersHolder () {workers.add ("John"); workers.add ("Susie"); workers.add ("Alex"); workers.add ("George"); salarisByWorkers.put ("John", 35000); salarisByWorkers.put ("Susie", 47000); salarisByWorkers.put ("Alex", 12000); salarisByWorkers.put ("George", 14000); } // Getters en setters}

Nu hebben we toegang tot de waarden van de collecties met behulp van SpEL:

@Value ("# {workersHolder.salaryByWorkers ['John']}") // 35000 privé geheel getal johnSalary; @Value ("# {workersHolder.salaryByWorkers ['George']}") // 14000 privé geheel getal georgeSalary; @Value ("# {workersHolder.salaryByWorkers ['Susie']}") // 47000 privé Integer susieSalary; @Value ("# {workersHolder.workers [0]}") // John private String firstWorker; @Value ("# {workersHolder.workers [3]}") // George private String lastWorker; @Value ("# {workersHolder.workers.size ()}") // 4 privé geheel getal numberOfWorkers;

3. Gebruik in veerconfiguratie

3.1. Verwijzen naar een boon

In dit voorbeeld bekijken we hoe u SpEL kunt gebruiken in op XML gebaseerde configuratie. Expressies kunnen worden gebruikt om naar bonen of bonenvelden / methoden te verwijzen. Stel dat we de volgende klassen hebben:

openbare klasse Engine {private int capaciteit; privé int paardenkracht; private int numberOfCylinders; // Getters and setters} public class Car {private String make; privé int-model; particuliere motorblok; privé int paardenkracht; // Getters en setters}

Nu maken we een toepassingscontext waarin expressies worden gebruikt om waarden te injecteren:

Bekijk de someCar Boon. De motor en paardenkracht velden van someCar gebruik uitdrukkingen die bean-verwijzingen zijn naar de motor boon en paardenkracht veld respectievelijk.

Om hetzelfde te doen met op annotaties gebaseerde configuraties, gebruikt u de @Value ("# {expression}") annotatie.

3.2. Operators gebruiken in configuratie

Elke operator uit de eerste sectie van dit artikel kan worden gebruikt in configuraties op basis van XML en annotaties. Onthoud echter dat we in op XML gebaseerde configuratie de operator voor punthaken "<" niet kunnen gebruiken. In plaats daarvan moeten we de alfabetische aliassen gebruiken, zoals lt (minder dan) of le (kleiner dan of gelijk aan). Voor op annotaties gebaseerde configuraties zijn er geen dergelijke beperkingen.

openbare klasse SpelOperators {private boolean gelijk; private boolean notEqual; private boolean groterThanOrEqual; private boolean en; private boolean of; private String addString; // Getters en setters
 @Override public String toString () {// toString die alle velden bevat}

Nu zullen we een spelOperators bean naar de toepassingscontext:

   = 6} "/> 300 of een auto.engine.capaciteit> 3000}" />

Als we die boon uit de context halen, kunnen we vervolgens controleren of de waarden correct zijn geïnjecteerd:

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

Hier kunnen we de uitvoer van het toString methode van spelOperators Boon:

[gelijk = waar, notEqual = onwaar, groterThanOrEqual = waar, en = waar, of = waar, addString = Een model vervaardigd door een ander merk] 

4. Expressies programmatisch parseren

Soms willen we expressies ontleden buiten de context van de configuratie. Gelukkig is dit mogelijk met SpelExpressionParser. We kunnen alle operatoren gebruiken die we in eerdere voorbeelden hebben gezien, maar zouden ze zonder accolades en hash-symbool moeten gebruiken. Dat wil zeggen, als we een uitdrukking willen gebruiken met de + operator bij gebruik in de Spring-configuratie, is de syntaxis #{1 + 1}; wanneer gebruikt buiten de configuratie, is de syntaxis eenvoudig 1 + 1.

In de volgende voorbeelden gebruiken we de Auto en Motor bonen gedefinieerd in de vorige sectie.

4.1. Gebruik makend van ExpressionParser

Laten we naar een eenvoudig voorbeeld kijken:

ExpressionParser expressionParser = nieuwe SpelExpressionParser (); Expression expression = expressionParser.parseExpression ("'Elke tekenreeks'"); String resultaat = (String) expression.getValue (); 

ExpressionParser is verantwoordelijk voor het parseren van expressietekenreeksen. In dit voorbeeld evalueert de SpEL-parser eenvoudig de tekenreeks ‘Elke tekenreeks ' als een uitdrukking. Het is niet verwonderlijk dat het resultaat zal zijn ‘Elke tekenreeks '.

Net als bij het gebruik van SpEL in configuratie, kunnen we het gebruiken om methoden aan te roepen, toegang te krijgen tot eigenschappen of constructors aan te roepen.

Expressie expression = expressionParser.parseExpression ("'Elke string'.length ()"); Integer resultaat = (Geheel getal) expression.getValue ();

Bovendien zouden we, in plaats van rechtstreeks op de letterlijke te werken, de constructor kunnen aanroepen:

Expression expression = expressionParser.parseExpression ("nieuwe String ('Elke string'). Length ()");

We hebben ook toegang tot het bytes eigendom van Draad class, op dezelfde manier, wat resulteert in de byte [] representatie van de string:

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

We kunnen methodeaanroepen koppelen, net als in normale Java-code:

Expressie expression = expressionParser.parseExpression ("'Willekeurige string'. Vervang (\" \ ", \" \ "). Length ()"); Integer resultaat = (Geheel getal) expression.getValue ();

In dit geval is het resultaat 9, omdat we de witruimte hebben vervangen door de lege tekenreeks. Als we het resultaat van de expressie niet willen casten, kunnen we de generieke methode gebruiken T getValue (Class wantedResultType), waarin we het gewenste type klasse kunnen opgeven dat we willen retourneren. Let daar op EvaluationException wordt gegenereerd als de geretourneerde waarde niet kan worden gecast wantedResultType:

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

Het meest gebruikelijke gebruik is om een ​​expressiestring op te geven die wordt geëvalueerd op basis van een specifieke objectinstantie:

Auto auto = nieuwe auto (); car.setMake ("Goede fabrikant"); car.setModel ("Model 3"); car.setYearOfProduction (2014); ExpressionParser expressionParser = nieuwe SpelExpressionParser (); Expression expression = expressionParser.parseExpression ("model"); EvaluationContext context = nieuwe StandardEvaluationContext (auto); String resultaat = (String) expression.getValue (context);

In dit geval is het resultaat gelijk aan de waarde van de model- veld van de auto voorwerp, "Model 3“. De StandardEvaluationContext class specificeert tegen welk object de uitdrukking zal worden geëvalueerd.

Het kan niet worden gewijzigd nadat het contextobject is gemaakt. StandardEvaluationContext is duur om te construeren, en tijdens herhaald gebruik bouwt het een cachestatus op waardoor daaropvolgende expressie-evaluaties sneller kunnen worden uitgevoerd. Vanwege caching is het een goede gewoonte om deze opnieuw te gebruiken StandardEvaluationContext waar het mogelijk is als het root-object niet verandert.

Als het root-object echter herhaaldelijk wordt gewijzigd, kunnen we het mechanisme gebruiken dat in het onderstaande voorbeeld wordt getoond:

Expression expression = expressionParser.parseExpression ("model"); String resultaat = (String) expression.getValue (auto);

Hier noemen we de getValue methode met een argument dat het object vertegenwoordigt waarop we een SpEL-expressie willen toepassen. We kunnen ook de generieke gebruiken getValue methode, net als voorheen:

Expression expression = expressionParser.parseExpression ("yearOfProduction> 2005"); booleaans resultaat = expression.getValue (auto, Boolean.class);

4.2. Gebruik makend van ExpressionParser om een ​​waarde in te stellen

De ... gebruiken setValue methode op de Uitdrukking object geretourneerd door het ontleden van een uitdrukking, kunnen we waarden instellen voor objecten. SpEL zorgt voor de typeconversie. Standaard gebruikt SpEL org.springframework.core.convert.ConversionService. We kunnen onze eigen aangepaste converter tussen typen maken. ConversionService is generiek bekend, dus het kan met generieke geneesmiddelen worden gebruikt. Laten we eens kijken hoe we het in de praktijk kunnen gebruiken:

Auto auto = nieuwe auto (); car.setMake ("Goede fabrikant"); car.setModel ("Model 3"); car.setYearOfProduction (2014); CarPark carPark = nieuwe CarPark (); carPark.getCars (). add (auto); StandardEvaluationContext context = nieuwe StandardEvaluationContext (carPark); ExpressionParser expressionParser = nieuwe SpelExpressionParser (); expressionParser.parseExpression ("cars [0] .model"). setValue (context, "Ander model");

Het resulterende auto-object zal hebben model-Ander model"Die is gewijzigd van"Model 3“.

4.3. Parser-configuratie

In het volgende voorbeeld zullen we de volgende klasse gebruiken:

openbare klasse CarPark {privélijst auto's = nieuwe ArrayList (); // Getter en setter}

Configureren is mogelijk ExpressionParser door de constructor aan te roepen met een SpelParserConfiguration voorwerp. Als we bijvoorbeeld proberen toe te voegen auto object in het auto's reeks van Parkeerplaats class zonder de parser te configureren, krijgen we de volgende foutmelding:

EL1025E: (pos 4): De collectie heeft '0' elementen, index '0' is ongeldig

We kunnen het gedrag van de parser veranderen, zodat deze automatisch elementen kan aanmaken als de gespecificeerde index null is (autoGrowNullReferences, de eerste parameter voor de constructor), of om automatisch een array of lijst te laten groeien om elementen te accommoderen die groter zijn dan de oorspronkelijke grootte (autoGrowCollections, de tweede parameter).

SpelParserConfiguration config = nieuwe SpelParserConfiguration (true, true); StandardEvaluationContext context = nieuwe StandardEvaluationContext (carPark); ExpressionParser expressionParser = nieuwe SpelExpressionParser (config); expressionParser.parseExpression ("auto's [0]"). setValue (context, auto); Auto resultaat = carPark.getCars (). Get (0);

Het resultaat auto object zal gelijk zijn aan de auto object dat is ingesteld als het eerste element van het auto's reeks van parkeerplaats object uit het vorige voorbeeld.

5. Conclusie

SpEL is een krachtige, goed ondersteunde expressietaal die voor alle producten in de Spring-portfolio kan worden gebruikt. Het kan worden gebruikt om Spring-applicaties te configureren of om parsers te schrijven om meer algemene taken in elke applicatie uit te voeren.

De codevoorbeelden in dit artikel zijn beschikbaar in de gekoppelde GitHub-opslagplaats.