Вирази-літерали

Підтримуються такі типи літеральних виразів: рядки, числові значення (int, real, hex), булеві та нульові. Рядки поділяються одинарними лапками. Щоб укласти саму лапку в рядок, використовуй два символи одинарної лапки.

У наступному лістингу показано просте використання літералів. Зазвичай вони використовуються не ізольовано, як у цьому випадку, а як частина складнішого виразу — наприклад, при використанні літералу з одного боку оператора логічного порівняння.

Java

ExpressionParser parser = new SpelExpressionParser();
// має значення "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
// виявляється 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
Object nullValue = parser.parseExpression("null").getValue();
Kotlin

val parser = SpelExpressionParser()
// має значення "Hello World"
val helloWorld = parser.parseExpression("'Hello World'").value як String
val avogadrosNumber = parser.parseExpression("6.0221415E+23").value як Double
// виходить 2147483647
val maxValue = parser.parseExpression("0x7FFFFFFF").value як Int
val trueValue = parser.parseExpression("true").value як Boolean
val nullValue = parser.parseExpression("null").value

Числа підтримують використання негативного знака, експоненційної форми представлення та десяткових крапок. За замовчуванням речові числа парсуються за допомогою Double.parseDouble().

Властивості, масиви, списки, асоціативні масиви та індексатори

Здійснювати навігацію за допомогою посилань на характеристики просто. Для цього використовуй крапку, щоб вказати значення вкладеної властивості. Примірники класу Inventor, pupin та tesla були заповнені даними. Щоб переміщатися "вниз" графом об'єктів і отримати рік народження tesla і місто народження pupin, ми використовуємо такі вирази:

Java

// виявляється 1856
int year = ( Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
Kotlin

// виявляється 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) як Int
val city = parser.parseExpression("placeOfBirth.city").getValue(context) як String

Нечутливість до регістру допускається для першої літери імен властивостей. Таким чином, вирази у наведеному вище прикладі можна записати як Birthdate.Year + 1900 та PlaceOfBirth.City, відповідно. Крім того, до властивостей можна звертатися через виклики методів - наприклад, getPlaceOfBirth().getCity() замість placeOfBirth.city.

Вміст масивів та списків можна отримати, використовуючи запис із використанням квадратних дужок, як показано в наступному прикладі:

Java

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// Масив винаходів
// має значення "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
        context, tesla, String.class);
// Список членів
// має значення "Nikola Tesla".
String name = parser.parseExpression("members[0].name").getValue(
        context, ieee, String.class);
// Список членів
// має значення "Wireless communication"
String invention = parser.parseExpression("members[0].inventions[6]").getValue(
        context, ieee, String.class)
Kotlin

val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
// Масив винаходів
// має значення "Induction motor"
val invention = parser.parseExpression("inventions[3]").getValue(
        context, tesla, String::class.java)
// Список членів
// має значення "Nikola Tesla".
val name = parser.parseExpression("members[0].name").getValue(
        context, ieee, String::class.java)
// Список членів
// має значення "Wireless communication"
val invention = parser.parseExpression(" members[0].inventions[6]").getValue(
        context, ieee, String::class.java)

Вміст асоціативних масивів можна отримати, вказавши літеральне значення ключа у дужках. У наступному прикладі, оскільки ключі для асоціативного масиву officers є рядками, ми можемо вказати рядкові літерали:

Java

// Офіцерський словник
Inventor pupin = parser.parseExpression("officers['president']").getValue(
        societyContext, Inventor.class);
// має значення "Idvor"
String city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
        societyContext, String.class);
// установка значень
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
        societyContext, "Croatia");
Kotlin

// Офіцерський словник
val pupin = parser.parseExpression("officers['president']").getValue(
        societyContext, Inventor::class.java)
// має значення "Idvor"
val city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
        societyContext, String::class.java)
// встановлення значень
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
        societyContext, "Croatia")

Списки, що вбудовуються

Можна безпосередньо виражати списки у виразі, використовуючи нотацію {}.

Java

// визначається як Java-список, що містить чотири числа
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
Kotlin

// визначається як Java-список, що містить чотири числа
val numbers = parser.parseExpression("{1,2,3,4}" ).getValue(context) as List<*>
val listOfLists = parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context) as List<*>

{} означає порожній список. З міркувань продуктивності, якщо список повністю складається з фіксованих літералів, для представлення виразу створюється список констант (замість того, щоб робити новий список при кожному обчисленні).

Map, що вбудовуються

Також можна безпосередньо виразити Map у виразі, використовуючи нотацію {key:value}. У цьому прикладі показано, як це зробити:

Java

// визначається як Java Map, що містить два записи
Map inventorInfo = (Map) parser.parseExpression("{ name:'Nikola',dob:'10-July-1856'}").getValue(context);
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue (context);
Kotlin

// визначається як Java Map, що містить два записи
val inventorInfo = parser. parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context) as Map<*, *>
val mapOfMaps = parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context) as Map<*, *>

{:} сама по собі означає порожній Map. З міркувань продуктивності, якщо Map сама складається з фіксованих літералів чи інших вкладених константних структур (списків чи Map), для подання виразу створюється Map констант (а чи не будується новий Map при кожному обчисленні). Укладати ключі Map у лапки необов'язково (якщо ключ не містить крапку (.)). У наведених вище прикладах не використовуються лапки.

Влаштування масивів

Можна створювати масиви, використовуючи звичний синтаксис Java, опційно вказуючи ініціалізатор, щоб масив заповнювався під час побудови. У цьому прикладі показано, як це зробити:

Java

int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue (context);
// Масив з ініціалізатором
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
// Багатовимірний масив
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
Kotlin

val numbers1 = parser.parseExpression("new int[4]").getValue(context) as IntArray
// Масив з ініціалізатором
val numbers2 = parser.parseExpression("new int[]{1,2,3}").getValue(context) as IntArray
// Багатовимірний масив
val numbers3 = parser.parseExpression("new int[4][5]").getValue( context) as Array<IntArray>

Наразі не можна вказувати ініціалізатор при створенні багатовимірного масиву.

Методи

Можна викликати методи, використовуючи типовий синтаксис програмування Java. Також можна викликати методи для літералів. Також підтримуються змінні аргументи. У таких прикладах показано, як викликати методи:

Java

// рядковий літерал, що має значення "bc"
String bc = parser.parseExpression("'abc'). substring(1, 3)").getValue(String.class);
// виявляється true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue( societyContext, Boolean.class);
Kotlin

// рядковий літерал, що має значення "bc"
val bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String::class. java)
// виявляється true
val isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue( societyContext, Boolean::class.java)

Оператори

Мова виразів Spring Expression Language підтримує такі види операторів:

  • Реляційні оператори

  • Логічні оператори

  • Математичні оператори

  • Оператор присвоєння

Реляційні оператори

Оператори відношень (рівно (equal), не дорівнює (not equal), менше ніж (less than), менше ніж або дорівнює (less than or equal), більше ніж (greater than) і більше ніж або дорівнює (greater than or equal)) підтримуються за допомогою стандартної нотації оператора. У наступному лістингу наведено кілька прикладів операторів:

Java

// виявляється true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean. class);
// виявляється false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
// виявляється true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
Kotlin

// виявляється true
val trueValue = parser.parseExpression("2 == 2").getValue(Boolean::class.java)
// виявляється false
val falseValue = parser.parseExpression( "2 < -5.0").getValue(Boolean::class.java)
// виявляється true
val trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean::class.java) 

Порівняння "більше ніж" і "менше ніж" з null підкоряються простому правилу: null вважається нічим (це НЕ нуль). Як наслідок, будь-яке інше значення завжди більше null(X > null завжди true) і ніяке інше значення ніколи не менше ніж ніщо (X < null завжди false).

Якщо надаєш перевагу числовим порівнянням, уникай null порівнянь на основі чисел на користь порівнянь з нулем (наприклад, X >0 або X <0).

На додаток до стандартних операторів відносин SpEL підтримує оператор instanceof та оператор matches на основі регулярних виразів. У цьому списку наведено приклади обох варіантів:

Java
// виявляється false
boolean falseValue = parser.parseExpression(
        "'xyz' instanceof T(Integer)").getValue(Boolean.class);
// виявляється true
boolean trueValue = parser.parseExpression(
        "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
// виявляється false
boolean falseValue = parser.parseExpression(
        "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
Kotlin

// виявляється false
val falseValue = parser.parseExpression(
        "'xyz' instanceof T(Integer)").getValue(Boolean::class.java)
// виявляється true
val trueValue = parser.parseExpression(
        "'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean::class.java)
// виявляється false
val falseValue = parser.parseExpression(
        "'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue (Boolean::class.java)
Обережно з примітивними типами, оскільки вони відразу упаковуються у свої типи-обгортки. Наприклад, 1 instanceof T(int) виявляється false, а 1 instanceof T(Integer) виявляється true, як і очікувалося.

Кожен символьний оператор також може бути вказаний у вигляді суто літерного еквівалента. Це дозволяє уникнути проблем, коли символи, що використовуються, мають особливе значення для типу документа, в який вбудовується вираз (наприклад, в XML-документ). Текстові еквіваленти такі:

  • lt (<)

  • gt (>)

  • le (<=)

  • ge (>=)

  • eq (==)

  • ne (!=)

  • div (/)

  • mod (%)

  • not (!)

Всі текстові оператори не чутливі до регістру.

Логічні оператори

SpEL підтримує наступні логічні оператори:

  • and (&&)

  • or (||)

  • not (!)

У цьому прикладі показано, як використовувати логічні оператори:

Java

// -- AND --
// виявляється false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
// виявляється true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
// -- OR --
// виявляється true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);
// виявляється true
String expression = "isMember('Nikola Tesla') або isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
// -- NOT --
// виявляється false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
// - AND and NOT -
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
Kotlin

// -- AND --
// виявляється false
val falseValue = parser.parseExpression("true and false").getValue(Boolean::class.java)
// виявляється true
val expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"
val trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)
// -- OR --
// виявляється true
val trueValue = parser.parseExpression("true or false").getValue(Boolean::class.java)
// виявляється true
val expression = "isMember('Nikola Tesla') або isMember('Albert Einstein')", Boolean::class.java)
// -- NOT --
// виявляється false
val falseValue = parser.parseExpression("!true").getValue(Boolean::class.java)
// -- AND and NOT --
val expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"
val falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean::class.java)

Математичні оператори

Оператор додавання (+) можна використовувати як для чисел, так і для рядків. Оператори віднімання (-), множення (*) та ділення (/) можна використовувати тільки для чисел. Також можна використовувати оператори взяття залишку (%) та експоненційного зростання (^) для чисел. Використовується стандартний пріоритет операторів. У наступному прикладі показані використовувані математичні оператори:

Java

// Додавання int two = parser.parseExpression("1 + 1").getValue(Integer.class) ; // 2
String testString = parser.parseExpression(
        "'test' + '' + 'string'").getValue(String.class); // 'test string'
// Віднімання
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
// Розмноження
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
// Розподіл
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2
double one = parser.parseExpression("8.0/4e0/2").getValue(Double.class); // 1.0
// Взяття залишку int three = parser.parseExpression("7 % 4"). getValue(Integer.class); // 3
int one = parser.parseExpression("8 / 5% 2").getValue(Integer.class); // 1
// Пріоритет операторів
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
Kotlin

// Додавання
val two = parser.parseExpression("1 + 1" ).getValue(Int::class.java) // 2
val testString = parser.parseExpression( "'test' + '' + 'string'").getValue(String::class.java) // 'test string'
// Віднімання val four = parser.parseExpression("1 - -3").getValue(Int::class.java) // 4
val d = parser.parseExpression("1000.00 - 1e4").getValue(Double::class .java) // -9000
// Розмноження
val six = parser.parseExpression("-2 * -3").getValue(Int::class.java) // 6
val twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double::class.java) // 24.0
// Поділ
val minusTwo = parser.parseExpression("6 / -3"). parser.parseExpression("8.0 / 4e0 / 2").getValue(Double::class.java) // 1.0
// Взяття залишку
val three = parser.parseExpression("7 % 4").getValue(Int::class. java) // 3
val one = parser.parseExpression("8 / 5 % 2").getValue(Int::class.java) // 1
// Пріоритет операторів
val minusTwentyOne = parser.parseExpression("1+2-3 *8").getValue(Int::class.java) // -21

Оператор присвоєння

Щоб встановити властивість, використовуй оператор присвоєння (=). Зазвичай це робиться всередині виклику setValue, але може бути зроблено і всередині виклику getValue. У наступному лістингу показано обидва способи використання оператора присвоєння:

Java

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic");
// альтернатива
String aleks = parser.parseExpression(
        "name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);
Kotlin

val inventor = Inventor()
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic")
// альтернатива
val aleks = parser.parseExpression(
        "name = 'Aleksandar Seovic'").getValue(context, inventor, String::class.java)

Типи

Можна використовувати спеціальний оператор T для зазначення екземпляра java.lang.Class (тип). Статичні методи викликаються за допомогою цього оператора. StandardEvaluationContext використовує TypeLocator для пошуку типів, а StandardTypeLocator (який може бути замінений) побудований з урахуванням особливостей пакета java.lang. Це означає, що посилання T() на типи з пакета java.lang не потрібно повністю уточнювати, однак усі інші посилання на типи мають бути уточнені. У цьому прикладі показано, як використовувати оператор T:

Java

Class dateClass = parser.parseExpression("T(java.util. Date)").getValue(Class.class);
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
boolean trueValue = parser.parseExpression(
        "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
        .getValue(Boolean.class);
Kotlin

val dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class::class.java)
val stringClass = parser.parseExpression("T(String)").getValue(Class::class.java)
val trueValue = parser.parseExpression(
        "T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode) ).FLOOR")
        .getValue(Boolean::class.java)

Конструктори

Можна викликати конструктори за допомогою оператора new. Потрібно використовувати повністю уточнене ім'я класу для всіх типів, крім тих, що знаходяться в пакеті java.lang(Integer, Float, String і так далі). У цьому прикладі показано, як використовувати оператор new для виклику конструкторів:

Java
Inventor einstein = p.parseExpression(
        "new org. spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
        .getValue(Inventor.class);
// створюємо новий екземпляр Inventor у методі add() List
p.parseExpression(
         "Members.add(new org.spring.samples.spel.inventor.Inventor(
            'Albert Einstein', 'German'))").getValue(societyContext );
Kotlin

val einstein = p.parseExpression(
        "new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
        .getValue(Inventor::class.java)
// створюємо новий екземпляр Inventor у методі add() List
p.parseExpression(
        "Members.add(new org.spring .samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
        .getValue(societyContext)

Змінні

Ти можеш посилатися на змінні у виразі за допомогою синтаксису #variableName. Змінні встановлюються за допомогою методу setVariable у реалізації EvaluationContext.

Допустимі імена змінних повинні складатися з одного або декількох із зазначених нижче підтримуваних символів a до z

  • цифри: від 0 до 9

  • підкреслення: _

  • знак долара: $

У наступному прикладі показано, як використовувати змінні.

Java
Inventor tesla = new Inventor( "Nikola Tesla", "Serbian");
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("newName", "Mike Tesla");
parser.parseExpression("name = #newName").getValue(context, tesla);
System.out.println(tesla.getName()) // "Mike Tesla"
Kotlin
val tesla = Inventor("Nikola Tesla", "Serbian")
val context = SimpleEvaluationContext.forReadWriteDataBinding().build()
context.setVariable("newName", "Mike Tesla")
parser.parseExpression("name = #newName").getValue(context, tesla)
println(tesla.name) // "Mike Tesla"

Змінні #this та #root

Змінна #this завжди визначена та посилається на поточний об'єкт обчислення (щодо якого дозволяються некваліфіковані посилання). Змінна #root завжди визначена та посилається на об'єкт кореневого контексту. Хоча #this може змінюватися в міру обчислення компонентів виразу, #root завжди належить до кореня. У наступних прикладах показано, як використовувати змінні #this та #root:

Java

// створюємо масив цілих чисел
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
// Створюємо синтаксичний аналізатор і встановлюємо змінну "primes" як масив цілих чисел
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess();
context.setVariable("primes", primes);
// Усі прості числа > 10 зі списку (використовуючи вибірку ?{...})
// виявляється [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
        "#primes.?[#this>10]").getValue(context);;
Kotlin

// створюємо масив цілих чисел
val primes = ArrayList<Int>() primes.addAll(listOf(2, 3, 5, 7, 11, 13, 17))
// створюємо синтаксичний аналізатор і задаємо змінну "primes" як масив цілих чисел
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataAccess()
context.setVariable("primes", primes)
// усі прості числа 10 зі списку (використовуючи вибірку ?{...})
// виявляється [11, 13, 17]
val primesGreaterThanTen = parser.parseExpression(
        "#primes.?[#this>10]").getValue(context) as List< ;Int>

Функції

Ти можеш розширити SpEL, зареєструвавши обумовлені користувачем функції, які можуть бути викликані в рядку виразу. Функція реєструється через EvaluationContext. У наступному прикладі показано, як зареєструвати функцію, яку визначає користувач:

Java
Method method = ...;
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", method);
            
Kotlin

val method: Method = ...
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("myFunction", method)

Наприклад, розглянемо наступний службовий метод, який інвертує рядок:

Java

public abstract class StringUtils {
    public static String reverseString(String input) {
        StringBuilder backwards = new StringBuilder(input.length());
        for (int i = 0; i < input.length(); i++) {
            backwards.append(input.charAt(input.length() - 1 - i));
        }
        return backwards.toString();
    }
}
Kotlin

fun reverseString(input: String): String {
    val backwards = StringBuilder(input.length)
    for (i in 0 until input.length) {
        backwards.append(input[input.length - 1 - i])
    }
    return backwards.toString()
}

Тому можна зареєструвати та використовувати попередній метод, як показано в наступному прикладі:

Java

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",
        StringUtils.class.getDeclaredMethod("reverseString", String.class));
String helloWorldReversed = parser.parseExpression(
        "#reverseString('hello')").getValue(context, String.class);
Kotlin

val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("reverseString", ::reverseString::javaMethod)
val helloWorldReversed = parser.parseExpression(
        "#reverseString('hello')").getValue(context, String::class.java)

Посилання на бін

Якщо контекст обчислення налаштований за допомогою розпізнавача бінів, можна здійснювати пошук бінів у виразі за допомогою символу @. У цьому прикладі показано, як це зробити:

Java

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// Це призведе до виклику resolve(context, "something") для MyBeanResolver під час обчислення
Object bean = parser.parseExpression("@something").getValue(context);
Kotlin

val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setBeanResolver(MyBeanResolver())
// Це призведе до виклику resolve(context, "something ") для MyBeanResolver під час обчислення
val bean = parser.parseExpression("@something").getValue(context)

Щоб отримати доступ до самого біна-фабрики, слід замість імені біна поставити символ &. У цьому прикладі показано, як це зробити:

Java

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// Це призведе до виклику resolve(context,"&foo") для MyBeanResolver під час обчислення
Object bean = parser.parseExpression("&foo").getValue(context);;
Kotlin

val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setBeanResolver(MyBeanResolver())
// Це призведе до виклику resolve(context ,"&foo") для MyBeanResolver під час обчислення
val bean = parser.parseExpression("&foo").getValue(context)

Тернарний оператор (If-Then-Else)

Можна використовувати тернарний оператор для виконання логіки умовного переходу if-then-else (якщо інакше) усередині виразу. У наступному лістингу наведено простий приклад:

Java

String falseString = parser.parseExpression(
        "false ? 'trueExp' : 'falseExp'").getValue(String.class);
Kotlin

val falseString = parser.parseExpression(
        "false ? 'trueExp' : 'falseExp'").getValue(String::class.java)

У даному випадку булеве false призводить до повернення строкового значення 'falseExp'. Далі слідує наближений до практики приклад:

Java

parser.parseExpression("name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");
expression = isMember(#queryName)? #queryName + ' is a member of the ' " +
        "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";
String queryResultString = parser.parseExpression(expression)
        .getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla є членом IEEE Society"
Kotlin

parser.parseExpression( "name").setValue(societyContext, "IEEE")
societyContext.setVariable("queryName", "Nikola Tesla")
expression = "isMember(#queryName)? #queryName + ' є членом ' " + "+ + 'Society' : #queryName + 'not a member of the ' + Name + 'Society'"
val queryResultString = parser.parseExpression(expression)
        .getValue(societyContext, String::class.java)
// queryResultString = "Nikola Tesla is a member of the IEEE Society"

Див. Наступний розділ про Елвіс-оператор для ще більш короткого синтаксису тернарного оператора.

Елвіс-оператор

Елвіс-оператор (Elvis operator) є скороченням синтаксису тернарного оператора і використовується у мові Groovy. При використанні синтаксису тернарного оператора зазвичай доводиться повторювати змінну двічі, як показано в наведеному нижче прикладі:


String name = "Elvis Presley";
String displayName = (name != null ? name : "Unknown");

Замість цього можна використовувати Елвіс-оператор (названий так через подібність із зачіскою Елвіса Преслі). У цьому прикладі показано, як використовувати Елвіс-оператор:

Java

ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(),String.class);
System.out.println(name); // 'Unknown'
Kotlin

val parser = SpelExpressionParser()
val name = parser.parseExpression(" name?:'Unknown'").getValue(Inventor(), String::class.java)
println(name) // 'Unknown'

У наступному лістингу показаний складніший приклад:

Java

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
String name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name);  // Nikola Tesla
tesla.setName(null);
name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name);  // Elvis Presley
Kotlin

val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
val tesla = Inventor("Nikola Tesla", "Serbian")
var name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java)
println(name)  // Nikola Tesla
tesla.setName(null)
name = parser.parseExpression("name?:'Elvis Presley'").getValue(context, tesla, String::class.java)
println(name)  // Elvis Presley

Ти можеш використовувати Елвіс-оператор для застосування значень за замовчуванням у виразах. Наступний приклад показує, як використовувати Елвіс-оператор у виразі, анотованим @Value:

@Value("#{systemProperties['pop3.port'] ?: 25}")

Таким чином впроваджується системна властивість pop3.port, якщо вона визначена, або 25, якщо ні.

Оператор безпечної навігації

Оператор безпечної навігації використовується для запобігання NullPointerException і походить з мови Groovy. Зазвичай, якщо є посилання на об'єкт, може знадобитися переконатися, що воно не є порожнім, перш ніж звертатися до методів чи властивостей об'єкта. Щоб уникнути цього, оператор безпечної навігації повертає null, а не генерує виняток. У цьому прикладі показано, як використовувати оператор безпечної навігації:

Java

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Inventor tesla = новий Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
String city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class); System.out.println(city); // Smiljan
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String.class);
System.out.println(city); // null - не генерує NullPointerException!!!
Kotlin

val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
val tesla = Inventor("Nikola Tesla", "Serbian")
tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan"))
var city = parser.parseExpression("place (context, tesla, String::class.java)
println(city) // Smiljan
tesla.setPlaceOfBirth(null)
city = parser.parseExpression("placeOfBirth?.city").getValue(context, tesla, String::class. java)
println(city) // null - не генерує NullPointerException!!!

Вибірка колекцій

Вибірка (selection) — це потужна функція мови виразів, яка дозволяє перетворювати вихідну колекцію на іншу колекцію шляхом вибірки з її записів.

Для вибірки використовується синтаксис .?[selectionExpression]. Він фільтрує колекцію та повертає нову колекцію, що містить підмножину вихідних елементів. Наприклад, за допомогою вибірки можна легко отримати список сербських винахідників, як показано в наступному прикладі:

-title">Java

List<Inventor> list = (List<Inventor>) parser.parseExpression(
        "members.?[nationality == 'Serbian']").getValue(societyContext);
Kotlin

val list = parser.parseExpression(
        "members.?[nationality == 'Serbian']").getValue(societyContext) as List<Inventor>

Вибірка підтримується для масивів і всього, що реалізує java.lang.Iterable або java.util.Map. Для списку або масиву критерії вибірки оцінюються за кожним окремим елементом. Щодо асоціативного масиву критерії вибірки оцінюються за кожним елементом асоціативного масиву (об'єкти Java типу Map.Entry). Кожен запис асоціативного масиву має key і value, доступні як властивості для використання при вибірці. асоціативного масиву, в яких значення елемента менше 27:

Java
Map newMap = parser.parseExpression("map.?[value<27]").getValue( );
Kotlin
val newMap = parser.parseExpression("map.?[value<27]" ).getValue()

Окрім повернення всіх вибраних елементів, можна отримувати тільки перший або останній елемент. Для отримання першого елемента, який відповідає критеріям вибірки, синтаксис має вигляд .^[selectionExpression]. Щоб отримати останній елемент, що задовольняє критеріям вибірки, синтаксис набуває вигляду .$[selectionExpression].

Проєкція колекцій

Проєкція (projection) дозволяє колекції управляти обчисленням виразу, а результатом є нова колекція. Синтаксис для проєкції має вигляд .![projectionExpression]. Наприклад, припустимо, що ми маємо список винахідників, але нам потрібен список міст, в яких вони народилися. По суті ми хочемо зробити обчислення 'placeOfBirth.city' для кожного запису в списку винахідників. У цьому прикладі для цього використовується проєкція:

Java
// повертає ['Smiljan', 'Idvor' ].
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
Kotlin
 // повертає ['Smiljan', 'Idvor'].
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>

Проєкція підтримується для масивів і всього, що реалізує java.lang.Iterable або java.util.Map. При використанні асоціативного масиву для управління проєкцією вираз проєкції обчислюється за кожним записом у цьому асоціативному масиві (представленої у вигляді Map.Entry мовою Java). Результатом проєкції за асоціативним масивом є список, що складається з обчислення проєкції для кожного запису асоціативного масиву.

Шаблонізація виразів

Шаблони виразів дозволяють змішувати буквений текст з одним або декількома блоками обчислень. Кожен блок обчислень розмежований символами префікса та суфікса, які можна визначити. Зазвичай як роздільники використовують #{ }, як показано в наступному прикладі:

Java
String randomPhrase = parser.parseExpression(
            "random number is #{T(java.lang.Math).random()}",
            new TemplateParserContext()).getValue(String.class);
// виходить "випадкове число дорівнює 0.7038186818312008"
Kotlin
val randomPhrase = parser.parseExpression(
            "random number is #{T(java.lang.Math).random()}",
            TemplateParserContext()).getValue(String::class.java)
// виходить "випадкове число дорівнює 0.7038186818312008"

Обчислення рядка здійснюється шляхом конкатенації буквеного тексту 'random number is ' з результатом обчислення виразу всередині роздільника #{ } (в цьому випадку результат виклику методу random()). Другий аргумент методу parseExpression() має тип ParserContext. Інтерфейс ParserContext використовується для того, щоб впливати на те, як буде синтаксично розібрано вираз, щоб забезпечити функціональність шаблонізації виразів. Далі слідує визначення TemplateParserContext:

Java

public class TemplateParserContext implements ParserContext {
    public String getExpressionPrefix() {
        return "#{";
    }
    public String getExpressionSuffix() {
        return "}";
    }
    public boolean isTemplate() {
        return true;
    }
}
Kotlin

class TemplateParserContext : ParserContext {
    override fun getExpressionPrefix(): String {
        return "#{"
    }
    override fun getExpressionSuffix(): String {
        return "}"
    }
    override fun isTemplate(): Boolean {
        return true
    }
}