Spring Web Flow — определение потоков (defining flows)
3. Определение потоков
3.1. Вступление
Эта глава начинает раздел пользователя. Здесь показано как реализовать потоки используя язык определения потоков (flow definition language). По окончанию этой главы вы будете должны обладать достаточным пониманием о языке построения и способны реализовать поток.
3.2. Что такое поток?
Поток инкапсулирует многоразово используемую последовательность шагов, которые могут выполняться в различных ситуациях. Диаграмма ниже иллюстрирует поток, который показывает шаги при бронировании отеля:
Карта сайта иллюстрирующая поток.
3.3. Какова структура обычного потока?
В Spring Web Flow поток состоит из ряда шагов под названием «state» (состояние). Вход в состояние, как правило, отображается для пользователя. На этом отображении (view-state) события перехватываются (обрабатываются) данным состоянием. События могут вызывать переходы в другие состояния (transition on=» « to= » «), которые определены в данном «view-state«.
Пример ниже показывает структуру потока book hotel из предыдущей диаграммы:
3.4 Как определяются потоки?
Потоки определяются разработчиками веб-приложения, используя обычный XML файл. Следующие шаги этого руководства покажут вам как это реализуется.
3.5 Основной язык элементов
3.5.1. flow
Каждый поток начинается со следующего корневого элемента (<flow .. /flow>):
1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> </flow> |
Все состояния потока определяются в пределах этого элемента. Определенное первоначальное состояние потока становится его отправной точкой.
3.5.2. view-state
Используйте view-state элемент для определения шагов в потоке, для отображения необходимых view:
1 |
<view-state id="enterBookingDetails" /> |
По соглашению, состояние (view-state) отображает свой идентификатор в шаблон, в котором находится поток. Например из примера выше состояние может отрисовать /WEB-INF/hotels/booking/enterBookingDetails.xhtml, если поток находится в директории /WEB-INF/hotels/booking.
3.5.3. transition
Используйте элемент <transition> для перехвата событий, происходящих в состоянии (view-state):
1 2 3 |
<view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> |
Это переход между состояниями (между разными view-state).
3.5.4. end-state
Используйте элемент <end-state> для определения выхода из потока:
1 |
<end-state id="bookingCancelled" /> |
Когда поток переходит в <end-state>, то он завершается и возвращает отображение.
3.5.5. Чекпоинт: Необходимые элементы языка
С тремя элементами view-state, transition и end-state, вы можете быстро реализовать логику навигации. Часто, команды реализуют её до добавления поведения потока, таким образом разработчики могут сосредоточиться на разработке пользовательского интерфейса приложения с конечными пользователями в начале. Ниже представлен пример потока, реализующего логику навигации с использованием этих элементов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> <view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> <view-state id="reviewBooking"> <transition on="confirm" to="bookingConfirmed" /> <transition on="revise" to="enterBookingDetails" /> <transition on="cancel" to="bookingCancelled" /> </view-state> <end-state id="bookingConfirmed" /> <end-state id="bookingCancelled" /> </flow> |
3.6. Actions
Большинство потоков нуждаются в большем, чем просто навигации. Обычно требуется добавление бизнес логики приложения или других действий.
Поток имеет несколько точек, в которых можно выполнять действия (рассмотрены в 6 главе):
- Старт потока (on-start)
- Вход в состояние (on-entry)
- Рендер представления (on-render)
- Выполнение перехода (transition execution)
- Выход из состояния (end-state)
- Завершение потока (on-exit)
Действия определяются с помощью краткого языка выражений. Spring Web Flow используется Unified EL по умолчанию. Следующие несколько разделов будут охватывать основные элементы языка для определения действий.
3.6.1. evaluate
Элементы action наиболее часто используются в элементе evaluate. Используйте элемент evaluate для вычисления выражения в точке (описаны выше) в пределах вашего потока. С помощью этого одиночного тега вы можете вызывать методы Spring бинов или других переменных потока. Пример:
1 |
<evaluate expression="entityManager.persist(booking)" /> |
Присвоение полученного результата
Если выражение возвращает значение, то оно может быть сохранено в модели данных потока под названием flowScope:
1 |
<evaluate expression="bookingService.findHotels(searchCriteria)" result="flowScope.hotels" /> |
Преобразование полученного результата
Если возвращаемое значение должно быть преобразовано, то необходимо указать нужный тип с помощью атрибута result-type:
1 2 |
<evaluate expression="bookingService.findHotels(searchCriteria)" result="flowScope.hotels" result-type="dataModel"/> |
3.6.2. Чекпоинт: flow actions
Теперь рассмотрим пример бронирования отеля с добавленными действиями:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> <input name="hotelId" /> <on-start> <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" /> </on-start> <view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> <view-state id="reviewBooking"> <transition on="confirm" to="bookingConfirmed" /> <transition on="revise" to="enterBookingDetails" /> <transition on="cancel" to="bookingCancelled" /> </view-state> <end-state id="bookingConfirmed" /> <end-state id="bookingCancelled" /> </flow> |
Этот поток создает при старте объект Booking в области видимости потока (flow scope). ID отеля для бронирования достается из атрибута <input> потока.
3.7. Input/Output Mapping
Каждый поток имеет четко определенный вход\выход. Потоки могут получить атрибуты когда они стартуют, а так же вернуть выходные атрибуты по своему завершению. В этом смысле поток концептуально похож на вызов метода с следующей сигнатурой:
1 |
FlowOutcome flowId(Map<String, Object> inputAttributes); |
… где FlowOutcome имеет следующую сигнатуру:
1 2 3 4 |
public interface FlowOutcome { public String getName(); public Map<String, Object> getOutputAttributes(); } |
3.7.5. input
Используйте элемент input для определения входных атрибутов:
1 |
<input name="hotelId" /> |
Входные значения сохраняются в области видимости потока под именем атрибута. В примере выше, входные данные будут сохранены в атрибуте под именем hotelId.
Определение типа входных данных
Используйте атрибут type для определения типа входных данных:
1 |
<input name="hotelId" type="long" /> |
Если входное значение не будет соответствовать указанному типу, то будет предпринята попытка преобразования к указанному типу.
Назначение входного значения
Используйте атрибут value, чтобы указать куда присвоить входное значение:
1 |
<input name="hotelId" value="flowScope.myParameterObject.hotelId" /> |
Если тип значения выражения может быть определён, то метаданные будут приведены к этому типу, если нет — необходимо задать атрибут type.
Пометка input как «требуется»
Используйте атрибут required для указания, что значение input не может быть null или пустым:
1 |
<input name="hotelId" type="long" value="flowScope.hotelId" required="true" /> |
3.7.2. output
Используйте элемент output для определения атрибута при выходе из потока. Выходные атрибуты указываются в состояниях end-state и определяют выходные результаты (outcomes) потока.
1 2 3 |
<end-state id="bookingConfirmed"> <output name="bookingId" /> </end-state> |
Выходные значения достаются из области видимости потока по имени атрибута. Из примера выше, при выходе из потока, значения будут присвоены переменной bookingId.
3.7.3. Чекпоинт: input/output mapping
Обзор примера потока бронирования отеля с использованием input\output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> <input name="hotelId" /> <on-start> <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" /> </on-start> <view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> <view-state id="reviewBooking"> <transition on="confirm" to="bookingConfirmed" /> <transition on="revise" to="enterBookingDetails" /> <transition on="cancel" to="bookingCancelled" /> </view-state> <end-state id="bookingConfirmed" > <output name="bookingId" value="booking.id"/> </end-state> <end-state id="bookingCancelled" /> </flow> |
Теперь поток принимает атрибут hotelId и возвращает bookingId в качестве выходного атрибута при подтверждении бронирования.
3.8. Переменные
Поток может объявить одну или несколько переменных экземпляра. Эти переменные выделяются при запуске потока. Любые @Autowired временные ссылки, удерживаемые переменными, так же переопределяется при возобновлении потока.
3.8.1. var
Используйте элемент var для объявления переменных потока:
1 |
<var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/> |
Убедитесь, что классы, содержащие переменные, реализуют java.io.Serializable и переменная экземпляра сохраняется между запросами потока.
3.9. Области видимости переменных
Web Flow может хранить переменные в одном из нескольких областей видимости:
3.9.1. Flow Scope
Flow scope выделяется при старте потока и уничтожается при его завершении. Согласно реализации по умолчанию, любой объект сохраненный в этой области видимости должен быть сериализован.
3.9.2 .View Scope
View Scope выделяется когда поток входит в состоянии view-state и уничтожается при выходе из состояния. View scope переменные можно увидеть только из этого состояния view-state. Согласно реализации по умолчанию, любой объект сохраненный в этой области видимости должен быть сериализован.
3.9.3. Request Scope
Request scope выделяется при вызове потока и уничтожается при возврате.
3.9.4. Flash Scope
Flash scope выделяется при старте потока, очищается при каждом рендере, и уничтожается при завершении потока. Согласно реализации по умолчанию, любой объект сохраненный в этой области видимости должен быть сериализован.
3.9.5. Conversation Scope
Conversation scope вызывается при старте родительского потока и уничтожается при его завершении. Conversation scope виден в родительском потоке, а так же для всех его подпотоков. Согласно реализации по умолчанию, объекты conversation scope сохраняются в HTTP сессии и должны быть сереализованы для конкретной сессии.
Использование этой области видимости часто определяется из контекста, например зависит от того где была определена переменная — при старте определения потока (flow scope), внутри состояния (view scope) и т.д.. В других случаях, например в EL выражениях и Java коде, необходимо указывать значение явно. Следующие разделы поясняют как это можно сделать.
3.10. Вызов подпотоков
Поток может вызвать другой поток в качестве подпотока. Поток будет ждать пока подпоток вернет выходное значение, а затем отреагирует на возвращаемое значение (outcome).
3.10.1. subflow-state
Используйте элемент subflow-state для вызова другого потока в качестве подпотока:
1 2 3 4 5 6 |
<subflow-state id="addGuest" subflow="createGuest"> <transition on="guestCreated" to="reviewBooking"> <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" /> </transition> <transition on="creationCancelled" to="reviewBooking" /> </subflow-state> |
В этом примере вызывается поток createGuest и затем ожидается возврат из него. Когда поток получит возвращаемое значение от guestCreated, новый гость будет добавлен к списку забронировавших отель гостей.
Передача данных в подпоток
Используйте элемент input для передачи данных в подпоток:
1 2 3 4 |
<subflow-state id="addGuest" subflow="createGuest"> <input name="booking" /> <transition to="reviewBooking" /> </subflow-state> |
Маппинг выходных данных подпотока
Просто обратитесь к выходному атрибуту подпотока по его имени на выходном переходе:
1 2 3 |
<transition on="guestCreated" to="reviewBooking"> <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" /> </transition> |
В этом примере guest — имя выходного атрибута, который возвращается выходным переходом guestCreated.
3.10.2. Чекпоинт: вызов подпотоков
Пример потока бронирования отеля с вызовом подпотока:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"> <input name="hotelId" /> <on-start> <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" /> </on-start> <view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> <view-state id="reviewBooking"> <transition on="addGuest" to="addGuest" /> <transition on="confirm" to="bookingConfirmed" /> <transition on="revise" to="enterBookingDetails" /> <transition on="cancel" to="bookingCancelled" /> </view-state> <subflow-state id="addGuest" subflow="createGuest"> <transition on="guestCreated" to="reviewBooking"> <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" /> </transition> <transition on="creationCancelled" to="reviewBooking" /> </subflow-state> <end-state id="bookingConfirmed" > <output name="bookingId" value="booking.id" /> </end-state> <end-state id="bookingCancelled" /> </flow> |
Поток вызывает подпоток createGuest для добавления нового гостя в гостевой список.
4One thought on “Spring Web Flow — определение потоков (defining flows)”
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.
Это загадочное «определенное» состояние — не что иное, как самое первое описанное в теге <flow> состояние