Spring Web Flow — язык выражений (EL — expression language)
4. Язык выражений
4.1 Введение
Web Flow использует EL (expression language) для доступа к модели данных и вызова действий. В этой главе вы познакомитесь с синтаксисом EL, конфигурацией и специальными EL переменными, которые вы можете получить из описания вашего потока.
EL используется для многих вещей внутри потока, включая:
- Доступ к клиентским данным, таким как объявленным входным данным (элемент input) потока или ссылкам к параметрам запроса (request parameters).
- Доступ к данным в RequestContext Web Flow таким как flowScope или currentEvent.
- Вызов методов объектов, управляемых Spring, с помощью действий (actions).
- Вычисление выражений таких как переходы, ID подпотоков и имена представлений.
EL так же используется для связки параметров к модели объектов и обратно, чтобы отрисовать форматированные поля формы из свойств объекта модели. Однако это не работает при использовании Web Flow и JSF, т.к. в этом случае применяется стандартный жизненный цикл компонентов JSF.
4.1.1 Типы выражений
Важно понять, что есть два типа выражений в Web Flow: стандартные выражения и шаблонные.
Стандартные выражения
Первое и наиболее часто используемое выражение — стандартное выражение. Выражения такого типа вычисляются непосредственно EL и не должны быть заключены в #{}. Например:
1 |
<evaluate expression="searchCriteria.nextPage()" /> |
Выражение сверху является стандартным выражением, которое вызывает метод nextPage() переменной searchCriteria. Если вы попытаетесь вложить это выражение в #{}, то получите IllegalArgumentException. В этом контексте разделитель {} рассматривается как излишний. Допустимым значением выражения expression является простая строка.
Шаблонные выражения
Второй тип выражений — шаблонные выражения. Шаблонные выражения позволяют смешивать текст с одним или более стандартным выражением. Каждый блок с стандартным выражением заключается в разделитель #{}. Пример:
1 |
<view-state id="error" view="error-#{externalContext.locale}.xhtml" /> |
Выражение выше — шаблонное выражение. Результат вычисления будет конкатенацией строк error- и .xhtml с результатом вычислинным в externalContext.locale. Как вы можете видеть разделители необходимы для отделения вычисляемого выражения от шаблона.
4.2. EL реализации
4.2.1 Spring EL
Начиная с версии 2.1 Web Flow использует Spring Expression Language (Spring EL). SpEL был создан для обеспечения единого, четко поддерживаемого языка выражений для всех продуктов Spring. Он поставляется как отдельный jar org.springframework.expression в Spring Framework. В уже существующих приложениях необходимо удалить зависимости org.jboss.el или org.ognl и использовать взамен org.springframework.expression. О переносимости читайте ниже.
4.2.1 Unified EL
В Web Flow 2.0 Unified EL был основным языком выражения с реализаций jboss-el. Использование Unified EL предполагает реализацию зависимости el-api, которая обычно предоставляется веб-контейнером (например Tomcat 6). SpEL теперь является языком выражений по умолчанию и рекомендуется к использованию. Однако существует возможность заменить его на Unified EL, если возникнет такая необходимость. Для этого необходимо подключить к конфигурации Spring WebFlowELExpressionParser в flow-builder-services:
1 2 3 4 5 6 7 |
<webflow:flow-builder-services expression-parser="expressionParser"/> <bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser"> <constructor-arg> <bean class="org.jboss.el.ExpressionFactoryImpl" /> </constructor-arg> </bean> |
Важно, если приложение использует собственный конвертер, то необходимо убедиться в том, что WebFlowELExpressionParser сконфигурирован с его использованием:
1 2 3 4 5 6 7 8 9 10 |
<webflow:flow-builder-services expression-parser="expressionParser" conversion-service="conversionService"/> <bean id="expressionParser" class="org.springframework.webflow.expression.el.WebFlowELExpressionParser"> <constructor-arg> <bean class="org.jboss.el.ExpressionFactoryImpl" /> </constructor-arg> <property name="conversionService" ref="conversionService"/> </bean> <bean id="conversionService" class="somepackage.ApplicationConversionService"/> |
4.2.3 OGNL
Указан как устаревший в Web Flow 2.4.
4.3. Переносимость EL
В общем, вы обнаружите, что SpEL, Unified EL и OGNL имеют очень похожий синтаксис.
Вот несколько небольших изменений, которые необходимо знать для перехода на Spring EL от Unified EL или OGNL:
- ${} меняется на #{}.
- Выражения тестирующие данное событие #{currentEvent == ‘submit’} должно быть изменено на #{currentEvent.id == ‘submit’}
- Вычисление свойств вроде #{currentUser.name} может вызвать NullPointerException без проверок вроде #{currentUser != null ? currentUser.name : null}. Поэтому лучше использовать такую безопасную запись #{currentUser?.name}.
Для более подробной информации о синтаксисе SpEL смотрите Language Reference.
4.4. Специальные переменные EL
Есть несколько неявных переменных, на которые вы можете ссылаться из потока. Эти переменные будут рассмотрены в этом разделе.
Имейте в виду это общее правило. Переменные относящиеся к области данных (flowScope, viewScope, requestScope, etc.) должны использоваться только при назначении новых переменных области видимости.
Например, при присвоении результата вызова метода bookingService.findHotels(searchCriteria) в новую переменную «hotels«, вы должны использовать префикс области видимости для того, чтобы Web Flow мог понять где ее хранить:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" ... > <var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" /> <view-state id="reviewHotels"> <on-render> <evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" /> </on-render> </view-state> </flow> |
Однако при установке в существующую переменную, такую как «searchCriteria» из примера ниже, вы ссылаетесь прямо на переменную без префикса какой-либо области видимости:
1 2 3 4 5 6 7 8 9 10 11 12 |
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" ... > <var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" /> <view-state id="reviewHotels"> <transition on="sort"> <set name="searchCriteria.sortBy" value="requestParameters.sortBy" /> </transition> </view-state> </flow> |
Ниже приведен список неявных переменных, на которые вы можете ссылаться из потока:
4.4.1 flowScope
Используйте flowScope для назначения переменной потока. Переменная выделяется при старте потока и уничтожается при его завершении. С реализацией по умолчанию, любой объект в этой переменной должен быть сериализован.
1 |
<evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" /> |
4.4.2 viewScope
Используйте viewScope для назначения переменной в области отображения. Переменная выделяется при входе в view-state и уничтожается при выходе. Переменная viewScope доступна только из конкретного view-state. С реализацией по умолчанию, любой объект в этой переменной должен быть сериализован.
1 2 3 4 |
<on-render> <evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels" result-type="dataModel" /> </on-render> |
4.4.3 requestScope
Используйте requestScope для определения переменной запроса. Выделяется при вызове потока и уничтожается после возврата.
1 |
<set name="requestScope.hotelId" value="requestParameters.id" type="long" /> |
4.4.4 flashScope
Используйте flashScope для кратковременных переменных. Выделяется при старте потока, очищается при каждом рендере и уничтожается при завершении потока. С реализацией по умолчанию, любой объект в этой переменной должен быть сериализован.
1 |
<set name="flashScope.statusMessage" value="'Booking confirmed'" /> |
4.4.5 conversationScope
Данная переменная выделяется при старте родительского потока и уничтожается когда при его завершении. Conversation scope доступна для родительского потока и всех его подпотоков. Согласно реализации по умолчанию, объекты conversation scope сохраняются в HTTP сессии и должны быть сереализованы для конкретной сессии.
1 |
<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" /> |
4.4.6 requestParameters
Используйте requestParameters для хранения параметров запросов клиента:
1 |
<set name="requestScope.hotelId" value="requestParameters.id" type="long" /> |
4.4.7 currentEvent
Используйте currentEvent для доступа к атрибутам текущего события
1 |
<evaluate expression="booking.guests.add(currentEvent.attributes.guest)" /> |
4.4.8 currentUser
currentUser используйте для доступа к прошедшему проверку Principal:
1 2 |
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" /> |
4.4.9 messageContext
Используйте messageContext для доступа к контексту поиска и создания сообщений выполняемого потока, в том числе сообщений об ошибках или успешном завершении.
1 |
<evaluate expression="bookingValidator.validate(booking, messageContext)" /> |
4.4.10 resourceBundle
Используйте для доступа к message resource.
1 |
<set name="flashScope.successMessage" value="resourceBundle.successMessage" /> |
4.4.11 flowRequestContext
Используйте flowRequestContext для доступа к RequestContext API, который представляет текущий запрос потока.
4.4.12 flowExecutionContext
Используйте flowExecutionContext для доступа к FlowExecutionContext API, который представляет текущее состояние потока.
4.4.13 flowExecutionUrl
Используйте flowExecutionUrl для доступа к текущему относительному URI для данного состояния view-state потока.
4.4.14 externalContext
Используйте externalContext для доступа к клиентской стороне, включая атрибуты сессии пользователя.
1 2 |
<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)" result="viewScope.hotels" /> |
4.5 Алгоритм поиска «Scope»
Как уже упоминалось ранее, при определении переменной потока, необходимо указывать область видимости. Пример:
1 |
<set name="requestScope.hotelId" value="requestParameters.id" type="long" /> |
При простом доступе к переменной в одной области видимости, указание области видимости не обязательно. Пример:
1 |
<evaluate expression="entityManager.persist(booking)" /> |
Когда область видимости не определена, как в случае booking выше, то используется алгоритм поиска области видимости. Алгоритм просматривает соответственно: request, flash, view, flow, а также conversation. Если ничего не найдено, то будет выброшено исключение EvaluationException.
0