Выражения-литералы
Поддерживаются следующие типы литеральных выражений: строки, числовые значения (int, real, hex), булевы и нулевые. Строки разделяются одинарными кавычками. Чтобы заключить саму кавычку в строку, используйте два символа одинарной кавычки.
В следующем листинге показано простое использование литералов. Обычно они используются не изолированно, как в данном случае, а как часть более сложного выражения – например, при использовании литерала с одной стороны оператора логического сравнения.
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();
val parser = SpelExpressionParser()
// имеет значение "Hello World"
val helloWorld = parser.parseExpression("'Hello World'").value as String
val avogadrosNumber = parser.parseExpression("6.0221415E+23").value as Double
// оказывается 2147483647
val maxValue = parser.parseExpression("0x7FFFFFFF").value as Int
val trueValue = parser.parseExpression("true").value as Boolean
val nullValue = parser.parseExpression("null").value
Числа поддерживают использование отрицательного знака, экспоненциальной формы представления и десятичных точек. По умолчанию вещественные числа парсятся с помощью Double.parseDouble()
.
Свойства, массивы, списки, ассоциативные массивы и индексаторы
Осуществлять навигацию с помощью ссылок на свойства просто. Для этого используйте точку, чтобы указать значение вложенного свойства. Экземпляры класса Inventor
, pupin
и tesla
, были заполнены данными. Чтобы перемещаться "вниз" по графу объектов и получить год рождения Теслы и город рождения Пупина, мы используем следующие выражения:
// оказывается 1856
int year = (Integer) parser.parseExpression("birthdate.year + 1900").getValue(context);
String city = (String) parser.parseExpression("placeOfBirth.city").getValue(context);
// оказывается 1856
val year = parser.parseExpression("birthdate.year + 1900").getValue(context) as Int
val city = parser.parseExpression("placeOfBirth.city").getValue(context) as String
Нечувствительность к регистру допускается для первой буквы имен свойств. Таким образом, выражения в приведенном выше примере можно записать как Birthdate.Year + 1900
и PlaceOfBirth.City
, соответственно. Кроме того, к свойствам можно обращаться через вызовы методов – например, getPlaceOfBirth().getCity()
вместо placeOfBirth.city
.
Содержимое массивов и списков можно получить, используя запись с использованием квадратных скобок, как показано в следующем примере:
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);
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
являются строками, мы можем указать строковые литералы:
// Офицерский словарь
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");
// Офицерский словарь
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-список, содержащий четыре числа
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
// определяется как 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 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);
// определяется как 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, опционально указывая инициализатор, чтобы массив заполнялся во время построения. В следующем примере показано, как это сделать:
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);
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. Вы также можете вызвать методы для литералов. Также поддерживаются переменные аргументы. В следующих примерах показано, как вызывать методы:
// строковый литерал, имеющий значение "bc"
String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
// оказывается true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
societyContext, Boolean.class);
// строковый литерал, имеющий значение "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)) поддерживаются при помощи стандартной нотации оператора. В следующем листинге приведены несколько примеров операторов:
// оказывается 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);
// оказывается 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
на основе регулярных выражений. В следующем списке приведены примеры обоих вариантов:
// оказывается 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);
// оказывается 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
(!
)
В следующем примере показано, как использовать логические операторы:
// -- 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') or 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);
// -- 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') or isMember('Albert Einstein')"
val trueValue = parser.parseExpression(expression).getValue(societyContext, 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)
Математические операторы
Оператор сложения (+
) можно использовать как для чисел, так и для строк. Операторы вычитания (-
), умножения (*
) и деления (/
) можно использовать только для чисел. Также можно использовать операторы взятия остатка (%
) и экспоненциального роста (^
) для чисел. Применяется стандартный приоритет операторов. В следующем примере показаны используемые математические операторы:
// Сложение
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
// Сложение
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").getValue(Int::class.java) // -2
val one = 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
. В следующем листинге показаны оба способа использования оператора присваивания:
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);
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
:
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);
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
для вызова конструкторов:
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);
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
и отa
доz
-
цифры: от
0
до9
-
подчеркивание:
_
-
знак доллара:
$
В следующем примере показано, как использовать переменные.
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"
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
:
// создаем массив целых чисел
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);
// создаем массив целых чисел
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
. В следующем примере показано, как зарегистрировать функцию, определяемую пользователем:
Method method = ...;
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", method);
val method: Method = ...
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("myFunction", method)
Например, рассмотрим следующий служебный метод, который инвертирует строку:
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();
}
}
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()
}
Затем можно зарегистрировать и использовать предыдущий метод, как показано в следующем примере:
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);
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
context.setVariable("reverseString", ::reverseString::javaMethod)
val helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String::class.java)
Ссылки на бин
Если контекст вычисления был сконфигурирован с помощью распознавателя бинов, вы сможете осуществлять поиск бинов в выражении с помощью символа @
. В следующем примере показано, как это сделать:
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// Это приведет к вызову resolve(context, "something") для MyBeanResolver во время вычисления
Object bean = parser.parseExpression("@something").getValue(context);
val parser = SpelExpressionParser()
val context = StandardEvaluationContext()
context.setBeanResolver(MyBeanResolver())
// Это приведет к вызову resolve(context, "something") для MyBeanResolver во время вычисления
val bean = parser.parseExpression("@something").getValue(context)
Чтобы получить доступ к самому бину-фабрике, следует вместо имени бина поставить символ &
. В следующем примере показано, как это сделать:
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// Это приведет к вызову resolve(context,"&foo") для MyBeanResolver во время вычисления
Object bean = parser.parseExpression("&foo").getValue(context);
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 (если-то-иначе) внутри выражения. В следующем листинге приведен простой пример:
String falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String.class);
val falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String::class.java)
В данном случае булево false
приводит к возвращению строкового значения 'falseExp'
. Далее следует более приближенный к практике пример:
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 is a member of the IEEE Society"
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'"
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");
Вместо этого вы можете использовать Элвис-оператора (назван так из-за сходства с прической Элвиса Пресли). В следующем примере показано, как использовать Элвис-оператор:
ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("name?:'Unknown'").getValue(new Inventor(), String.class);
System.out.println(name); // 'Unknown'
val parser = SpelExpressionParser()
val name = parser.parseExpression("name?:'Unknown'").getValue(Inventor(), String::class.java)
println(name) // 'Unknown'
В следующем листинге показан более сложный пример:
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
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, а не генерирует исключение. В следующем примере показано, как использовать оператор безопасной навигации:
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Inventor tesla = new 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!!!
val parser = SpelExpressionParser()
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
val tesla = Inventor("Nikola Tesla", "Serbian")
tesla.setPlaceOfBirth(PlaceOfBirth("Smiljan"))
var city = parser.parseExpression("placeOfBirth?.city").getValue(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]
. Он фильтрует коллекцию и возвращает новую коллекцию, содержащую подмножество исходных элементов. Например, с помощью выборки можно легко получить список сербских изобретателей, как показано в следующем примере:
List<Inventor> list = (List<Inventor>) parser.parseExpression(
"members.?[nationality == 'Serbian']").getValue(societyContext);
val list = parser.parseExpression(
"members.?[nationality == 'Serbian']").getValue(societyContext) as List<Inventor>
Выборка поддерживается для массивов и всего, что реализует java.lang.Iterable
или java.util.Map
. Для списка или массива критерии выборки оцениваются по каждому отдельному элементу. Относительно ассоциативного массива критерии выборки оцениваются по каждому элементу ассоциативного массива (объекты Java типа Map.Entry
). Каждая запись ассоциативного массива имеет key
и value
, доступные как свойства для использования при выборке.
Следующее выражение возвращает новый ассоциативный массив, состоящий из тех элементов исходного ассоциативного массива, в которых значение элемента меньше 27:
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
val newMap = parser.parseExpression("map.?[value<27]").getValue()
Помимо возврата всех выбранных элементов, можно получать только первый или последний элемент. Для получения первого элемента, удовлетворяющего критериям выборки, синтаксис имеет вид .^[selectionExpression]
. Чтобы получить последний элемент, удовлетворяющий критериям выборки, синтаксис принимает вид .$[selectionExpression]
.
Проекция коллекций
Проекция (projection) позволяет коллекции управлять вычислением подвыражения, а результатом является новая коллекция. Синтаксис для проекции имеет вид .![projectionExpression]
. Например, предположим, что у нас есть список изобретателей, но нам нужен список городов, в которых они родились. По сути, мы хотим произвести вычисление 'placeOfBirth.city' для каждой записи в списке изобретателей. В следующем примере для этого используется проекция:
// возвращает ['Smiljan', 'Idvor' ].
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
// возвращает ['Smiljan', 'Idvor' ].
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
Проекция поддерживается для массивов и всего, что реализует java.lang.Iterable
или java.util.Map
. При использовании ассоциативного массива для управления проекцией выражение проекции вычисляется по каждой записи в этом ассоциативном массиве (представленной в виде Map.Entry
на языке Java). Результатом проекции по ассоциативному массиву является список, состоящий из вычисления выражения проекции для каждой записи ассоциативного массива.
Шаблонизация выражений
Шаблоны выражений позволяют смешивать буквенный текст с одним или несколькими блоками вычислений. Каждый блок вычислений разграничен символами префикса и суффикса, которые вы можете определить. Обычно в качестве разделителей используется #{ }
, как показано в следующем примере:
String randomPhrase = parser.parseExpression(
"random number is #{T(java.lang.Math).random()}",
new TemplateParserContext()).getValue(String.class);
// получается "случайное число равно 0.7038186818312008"
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
:
public class TemplateParserContext implements ParserContext {
public String getExpressionPrefix() {
return "#{";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
}
class TemplateParserContext : ParserContext {
override fun getExpressionPrefix(): String {
return "#{"
}
override fun getExpressionSuffix(): String {
return "}"
}
override fun isTemplate(): Boolean {
return true
}
}
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ