Vodič za jezike Spring Expression

1. Pregled

Spring Expression Language (SpEL) moćan je jezik izraza koji podržava postavljanje upita i manipulaciju grafom objekta u vrijeme izvođenja. Može se koristiti s XML-om ili konfiguracijama Spring-a temeljenim na napomenama.

Na jeziku je dostupno nekoliko operatora:

TipOperateri
Aritmetika+, -, *, /,%, ^, div, mod
Relacijski, ==,! =, =, lt, gt, eq, ne, le, ge
Logičnoi, ili, ne, &&, ||,!
Uvjetno?:
Regexšibice

2. Operateri

Za ove ćemo primjere koristiti konfiguraciju koja se temelji na bilješkama. Više detalja o XML konfiguraciji možete pronaći u kasnijim odjeljcima ovog članka.

SpEL izrazi počinju s # i umotani su u zagrade: #{izraz}. Svojstva se mogu uputiti na sličan način, počevši od $ simbol i umotan u zagrade: $ {property.name}. Držači rezerviranih svojstava ne mogu sadržavati SpEL izraze, ali izrazi mogu sadržavati reference svojstava:

# {$ {someProperty} + 2}

U gornjem primjeru pretpostavimo nekiVlasnik ima vrijednost 2, pa bi rezultirajući izraz bio 2 + 2, što bi se procijenilo na 4.

2.1. Aritmetički operatori

Podržani su svi osnovni aritmetički operatori.

@Value ("# {19 + 1}") // 20 privatnih dvostrukih dodavanja; @Value ("# {'String1' + 'string2'}") // "String1 string2" private String addString; @Value ("# {20 - 1}") // 19 privatno dvostruko oduzimanje; @Value ("# {10 * 2}") // 20 privatno dvostruko umnožavanje; @Value ("# {36/2}") // 19 privatna dvostruka podjela; @Value ("# {36 div 2}") // 18, isto kao za / operator private double divideAlphabetic; @Value ("# {37% 10}") // 7 privatnih dvostrukih modula; @Value ("# {37 mod 10}") // 7, isto kao i za% operator private double moduloAlphabetic; @Value ("# {2 ^ 9}") // 512 privatno dvostruko napajanje; @Value ("# {(2 + 2) * 2 + 9}") // 17 privatnih dvostrukih zagrada; 

Operacije dijeljenja i modula imaju abecedne pseudonime, div za / i mod za %. The + Operator se također može koristiti za spajanje nizova.

2.2. Relacijski i logički operatori

Podržane su i sve osnovne relacijske i logičke operacije.

@Value ("# {1 == 1}") // prava privatna logička vrijednost jednako; @Value ("# {1 eq 1}") // istinska privatna logička oznaka equAlphabetic; @Value ("# {1! = 1}") // lažno privatno logičko polje notEqual; @Value ("# {1 ne 1}") // lažno privatno logičko polje notEqualAlphabetic; @Value ("# {1 <1}") // lažno privatno logičko manjeThan; @Value ("# {1 lt 1}") // lažno privatno logičko ime lessThanAlphabetic; @Value ("# {1 1}") // lažno privatno logičko većeThan; @Value ("# {1 gt 1}") // lažno privatno logičko većeThanAlphabetic; @Value ("# {1> = 1}") // prava privatna logička vrijednost largerThanOrEqual; @Value ("# {1 ge 1}") // istinska privatna logička vrijednost largerThanOrEqualAlphabetic; 

Svi relacijski operatori također imaju abecedna pseudonima. Na primjer, u konfiguracijama temeljenim na XML-u ne možemo koristiti operatore koji sadrže kutne zagrade (<, <=,>, >=). Umjesto toga, možemo koristiti lt (manje od), le (manje ili jednako), gt (veće od), ili ge (veće ili jednako).

2.3. Logički operatori

SpEL podržava sve osnovne logičke operacije:

@Value ("#") // točno privatno logičko ili alfabetsko; @Value ("# {! True}") // lažno privatno logičko ne; @Value ("# {not true}") // lažno privatno logičko polje notAlphabetic;

Kao i kod aritmetičkih i relacijskih operatora, i svi logički operatori imaju abecedne klonove.

2.4. Uvjetni operateri

Uvjetni operatori koriste se za ubrizgavanje različitih vrijednosti ovisno o nekom stanju:

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

Ternarni operator koristi se za izvođenje kompaktne uvjetne logike if-then-else unutar izraza. U ovom primjeru pokušavamo provjeriti je li bilo pravi ili ne.

Druga uobičajena upotreba ternarnog operatora je provjera je li neka varijabla null a zatim vratite vrijednost varijable ili zadanu vrijednost:

@Value ("# {someBean.someProperty! = Null? SomeBean.someProperty: 'default'}" ") privatni niz ternarni;

Elvisov operater način je skraćivanja sintakse ternarnog operatora za gornji slučaj koji se koristi u jeziku Groovy. Dostupan je i u SpEL-u. Kôd u nastavku je ekvivalentan kodu iznad:

@Value ("# {someBean.someProperty?: 'Default'}") // Ubrizgat će navedeni uvjet ako je someProperty null private String elvis;

2.5. Korištenje regularnog izraza u SpEL-u

The šibice Operator se može koristiti za provjeru podudara li se niz s danim regularnim izrazom.

@Value ("# {'100' se podudara s \ d + '}") // točno privatno logičko vrijede validNumericStringResult; @Value ("# {'100fghdjf' matches '\ d +'}") // lažno privatno logičko logičko invalidNumericStringResult; @Value ("# {'važeći abecedni niz' podudara se '[a-zA-Z \ s] +'}") // istinska privatna logička vrijednost validAlphabeticStringResult; @Value ("# {'nevaljani abecedni niz # $ 1' podudara se '[a-zA-Z \ s] +'}") // lažno privatno logičko polje invalidAlphabeticStringResult; @Value ("# {someBean.someValue matches '\ d +'}") // true ako someValue sadrži samo znamenke private boolean validNumericValue;

2.6. Pristup Popis i Karta Predmeti

Uz pomoć SpEL-a možemo pristupiti sadržaju bilo kojeg Karta ili Popis u kontekstu. Stvorit ćemo novi grah radniciHolder koji će pohraniti podatke o nekim radnicima i njihovim plaćama u a Popis i a Karta:

@Component ("workerHolder") javna klasa WorkersHolder {privatni popis radnika = novi LinkedList (); privatna karta payByWorkers = novi HashMap (); javni WorkersHolder () {worker.add ("John"); worker.add ("Susie"); worker.add ("Alex"); radnici.add ("George"); payByWorkers.put ("John", 35000); payByWorkers.put ("Susie", 47000); payByWorkers.put ("Alex", 12000); payByWorkers.put ("George", 14000); } // Dobavljači i postavljači}

Sada vrijednostima zbirki možemo pristupiti pomoću SpEL-a:

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

3. Koristite u proljetnoj konfiguraciji

3.1. Navođenje graha

U ovom ćemo primjeru pogledati kako koristiti SpEL u konfiguraciji koja se temelji na XML-u. Izrazi se mogu koristiti za referenciranje graha ili polja / metoda graha. Na primjer, pretpostavimo da imamo sljedeće klase:

motor javne klase {private int capacity; privatni int horsePower; private int numberOfCylinders; // Getters and setters} javni razred automobila {private String make; privatni int model; privatni motor motora; privatni int horsePower; // Dobavljači i postavljači}

Sada kreiramo kontekst aplikacije u kojem se izrazi koriste za ubrizgavanje vrijednosti:

Pogledajte nekiAutomobil grah. The motor i konjMoć polja od nekiAutomobil koristite izraze koji su grah reference na motor grah i konjMoć polje odnosno.

Da biste učinili isto s konfiguracijama na temelju bilješki, upotrijebite @Vrijednost ("# {izraz}") bilješka.

3.2. Korištenje operatora u konfiguraciji

Svaki operator iz prvog odjeljka ovog članka može se koristiti u XML-u i konfiguracijama temeljenim na bilješkama. Međutim, imajte na umu da u konfiguraciji koja se temelji na XML-u ne možemo koristiti operator kutnih zagrada „<“. Umjesto toga, trebali bismo koristiti abecedna alias-imena, poput lt (manje od) ili le (manje ili jednako). Za konfiguracije temeljene na bilješkama ne postoje takva ograničenja.

javna klasa SpelOperators {private boolean jednak; privatna logička vrijednost notEqual; privatna logička vrijednost largerThanOrEqual; privatna logička i; private boolean ili; privatni niz addString; // Dobavljači i postavljači
 @Override javni niz toString () {// toString koji uključuje sva polja}

Sada ćemo dodati a spelOperators bean u kontekstu aplikacije:

   = 6} "/> 300 ili nekiCar.engine.capacity> 3000}" />

Dohvaćajući taj grah iz konteksta, tada možemo provjeriti jesu li vrijednosti pravilno ubačene:

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

Ovdje možemo vidjeti izlaz toString metoda spelOperators grah:

[jednako = istinito, notEqual = netačno, većeThanOrEqual = istinito i = točno, ili = točno, addString = Neki model proizveli Neki čine] 

4. Programsko raščlanjivanje izraza

Ponekad ćemo možda htjeti raščlaniti izraze izvan konteksta konfiguracije. Srećom, to je moguće, koristeći SpelExpressionParser. Možemo koristiti sve operatore koje smo vidjeli u prethodnim primjerima, ali trebali bismo ih koristiti bez zagrada i hash simbola. Odnosno, ako želimo upotrijebiti izraz s + kada se koristi u Spring konfiguraciji, sintaksa je #{1 + 1}; kada se koristi izvan konfiguracije, sintaksa je jednostavno 1 + 1.

U sljedećim ćemo primjerima koristiti Automobil i Motor grah definiran u prethodnom odjeljku.

4.1. Koristeći ExpressionParser

Pogledajmo jednostavan primjer:

ExpressionParser expressionParser = novi SpelExpressionParser (); Izraz izraza = expressionParser.parseExpression ("'Bilo koji niz'"); Rezultat niza = (Niz) expression.getValue (); 

ExpressionParser odgovoran je za raščlanjivanje nizova izraza. U ovom primjeru, SpEL parser jednostavno će procijeniti niz "Bilo koji niz" kao izraz. Ne iznenađuje što će rezultat biti "Bilo koji niz".

Kao i kod upotrebe SpEL-a u konfiguraciji, i mi ga možemo koristiti za pozivanje metoda, pristup svojstvima ili konstruktorima poziva.

Izraz izraza = expressionParser.parseExpression ("'Bilo koji niz'.length ()"); Integer rezultat = (Integer) expression.getValue ();

Uz to, umjesto da izravno operiramo literal, mogli bismo nazvati konstruktor:

Izraz izraza = expressionParser.parseExpression ("novi String ('Bilo koji niz'). Duljina ()");

Također možemo pristupiti bajtova vlasništvo Niz klase, na isti način, što rezultira bajtom [] predstavljanjem niza:

Izraz izraza = expressionParser.parseExpression ("'Bilo koji niz'. Bajtova"); bajt [] rezultat = (bajt []) expression.getValue ();

Pozive metoda možemo povezati lancima, baš kao u uobičajenom Java kodu:

Izraz izraza = expressionParser.parseExpression ("'Bilo koji niz'.replace (\" \ ", \" \ "). Length ()"); Integer rezultat = (Integer) expression.getValue ();

U ovom će slučaju rezultat biti 9, jer smo razmak zamijenili praznim nizom. Ako ne želimo emitirati rezultat izraza, možemo koristiti generičku metodu T getValue (klasa željenaResultType), u kojem možemo pružiti željenu vrstu klase koju želimo vratiti. Imajte na umu da EvaluationException bit će bačen ako vraćenu vrijednost nije moguće prebaciti na željeniResultType:

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

Najčešća je upotreba pružanje niza izraza koji se vrednuje prema određenoj instanci objekta:

Automobil = novi automobil (); car.setMake ("Dobar proizvođač"); car.setModel ("Model 3"); car.setYearOfProduction (2014); ExpressionParser expressionParser = novi SpelExpressionParser (); Izraz izraza = expressionParser.parseExpression ("model"); EvaluationContext context = novi StandardEvaluationContext (automobil); Rezultat niza = (Niz) expression.getValue (kontekst);

U tom će slučaju rezultat biti jednak vrijednosti znaka model polje automobil objekt, “Model 3“. The StandardEvaluationContext class određuje prema kojem će se objektu izraz ocjenjivati.

Ne može se mijenjati nakon stvaranja objekta konteksta. StandardEvaluationContext skupo je konstruirati, a tijekom ponovljene upotrebe izgrađuje predmemorirano stanje koje omogućuje brže izvršavanje naknadnih evaluacija izraza. Zbog predmemoriranja dobra je praksa ponovne upotrebe StandardEvaluationContext gdje je to moguće ako se korijenski objekt ne promijeni.

Međutim, ako se korijenski objekt mijenja više puta, možemo koristiti mehanizam prikazan u primjeru u nastavku:

Izraz izraza = expressionParser.parseExpression ("model"); Rezultat niza = (Niz) expression.getValue (automobil);

Ovdje zovemo getValue metoda s argumentom koji predstavlja objekt na koji želimo primijeniti SpEL izraz. Možemo koristiti i generički getValue metoda, baš kao i prije:

Izraz izraza = expressionParser.parseExpression ("yearOfProduction> 2005"); boolean rezultat = expression.getValue (auto, Boolean.class);

4.2. Koristeći ExpressionParser za postavljanje vrijednosti

Koristiti setValue metoda na Izraz objekt vraćen raščlanjivanjem izraza, možemo postaviti vrijednosti objektima. SpEL će se pobrinuti za pretvorbu tipa. Prema zadanim postavkama SpEL koristi org.springframework.core.convert.ConversionService. Možemo stvoriti vlastiti prilagođeni pretvarač između vrsta. ConversionService je svjestan generičkih lijekova, pa se može koristiti s generičkim lijekovima. Pogledajmo kako to možemo koristiti u praksi:

Automobil = novi automobil (); car.setMake ("Dobar proizvođač"); car.setModel ("Model 3"); car.setYearOfProduction (2014); CarPark carPark = novi CarPark (); carPark.getCars (). add (car); StandardEvaluationContext context = novi StandardEvaluationContext (carPark); ExpressionParser expressionParser = novi SpelExpressionParser (); expressionParser.parseExpression ("automobili [0] .model"). setValue (kontekst, "Drugi model");

Dobiveni objekt automobila imat će modelDrugi model"Koji je promijenjen iz"Model 3“.

4.3. Konfiguracija raščlanjivača

U sljedećem ćemo primjeru koristiti sljedeću klasu:

javna klasa CarPark {privatni popis automobila = novi ArrayList (); // Dobavljač i postavljač}

Moguće je konfigurirati ExpressionParser pozivanjem konstruktora s a SpelParserConfiguration objekt. Na primjer, ako pokušamo dodati automobil objekt u automobili niz od Parkiralište klase bez konfiguriranja parsera, dobit ćemo pogrešku poput ove:

EL1025E: (poz. 4): Zbirka ima elemente '0', indeks '0' nije važeći

Možemo promijeniti ponašanje parsera kako bismo mu omogućili da automatski stvara elemente ako je navedeni indeks null (autoGrowNullReferences, prvi parametar konstruktoru) ili za automatsko rast niza ili popisa za smještaj elemenata izvan njegove početne veličine (autoGrowCollections, drugi parametar).

SpelParserConfiguration config = novi SpelParserConfiguration (true, true); StandardEvaluationContext context = novi StandardEvaluationContext (carPark); ExpressionParser expressionParser = novi SpelExpressionParser (config); expressionParser.parseExpression ("automobili [0]"). setValue (kontekst, automobil); Rezultat automobila = carPark.getCars (). Get (0);

Dobivena automobil objekt će biti jednak automobil objekt koji je postavljen kao prvi element automobili niz od parkiralište objekt iz prethodnog primjera.

5. Zaključak

SpEL je moćan, dobro podržan jezik izražavanja koji se može koristiti u svim proizvodima iz proljetnog portfelja. Može se koristiti za konfiguriranje Spring aplikacija ili za pisanje parsera za izvršavanje općih zadataka u bilo kojoj aplikaciji.

Uzorci koda u ovom članku dostupni su u povezanom spremištu GitHub.