Функция Faces Flows JavaServer Faces позволяет создавать набор страниц с областью видимости FlowScoped
, которая больше области видимости запроса, но меньше области видимости сессии. Например, вы можете создать серию страниц для оформления заказа в онлайн-магазине. Вы можете создать набор автономных страниц, которые могут быть перенесены из одного магазина в другой при необходимости.
Faces Flow в некоторой степени аналогичны процедурам процедурного программирования.
-
Как и процедура, Flow имеет чётко определённую точку входа, список параметров и возвращаемое значение. Однако, в отличие от процедуры, Flow может возвращать несколько значений.
-
Как и процедура, Flow имеет область видимости, позволяющую получать информацию только во время вызова Flow. Такая информация недоступна вне области видимости Flow и не потребляет никаких ресурсов после возврата из Flow.
-
Как и процедура, Flow может вызвать другие Flow перед возвратом. Вызов Flow поддерживается в стеке вызовов: новый Flow помещается в стек вызова, а после возврата извлекается из него.
Приложение может иметь любое количество Flow. Каждый Flow включает в себя набор страниц и, как правило, один или несколько Managed-бинов, ограниченных областью видимости этого Flow. Каждый Flow имеет начальную точку, называемую начальным узлом, и точку выхода, называемую возвратным узлом.
Данные во Flow ограничиваются только этим Flow, но вы можете передавать данные из одного Flow в другой, задав параметры и вызвав другой Flow.
Flow могут быть вложенными, так что если вы вызываете один Flow из другого и затем выходите из второго Flow, вы возвращаетесь к вызывающему Flow, а не к возвратному узлу второго Flow.
Вы можете настроить Flow программно, создав аннотированный класс @FlowDefinition
, или вы можете настроить Flow с помощью файла конфигурации. Файл конфигурации может содержать один Flow, или вы можете использовать файл faces-config.xml
, чтобы поместить все Flow в одно место, если в вашем приложении много Flow. Программная конфигурация размещает код ближе к остальной части кода Flow и позволяет вам организовывать Flow в модули.
На рисунке 16-1 показаны два Flow и показано, как они взаимодействуют
Рис. 16-1. Два Faces Flow и их взаимодействие

На этом рисунке Flow A имеет начальный узел с именем flow-a
и две дополнительные страницы, next_a1
и next_a2
. Из next_a2
пользователь может либо выйти из Flow, используя узел возврата taskFlowReturn1
, либо вызвать Flow B, передав два параметра. Flow A также определяет два входящих параметра, которые он может принимать из Flow B. Flow B идентичен Flow A во всём, за исключением имён и файлов. Каждый Flow также имеет связанный Managed-бин. Бинами являются Flow_a_Bean
и Flow_b_Bean
.
Упаковка Flows в приложение
Как правило, Flow упаковывается в веб-приложении с использованием структуры каталогов, когда Flow отделены друг от друга. Например, в каталоге src/main/webapp
проекта Maven вы поместите файлы Facelets, которые находятся вне Flow, на верхний уровень, как обычно. Тогда файлы webapp
каждого Flow будут находиться в отдельных каталах, а файлы Java будут находиться в src/main/java
. Например, файлы для приложения, показанные на Рис. 16-1, могут выглядеть следующим образом:
src/main/webapp/
index.xhtml
return.xhtml
WEB_INF/
beans.xml
web.xml
flow-a/
flow-a.xhtml
next_a1.xhtml
next_a2.xhtml
flow-b/
flow-b-flow.xml
next_b1.xhtml
next_b2.xhtml
src/main/java/javaeetutorial/flowexample
FlowA.java
Flow_a_Bean.java
Flow_b_Bean.java
В этом примере flow-a
определяется программно в FlowA.java
, а flow-b
определяется файлом конфигурации flow-b-flow.xml
.
Простейший Flow: пример simple-flow
Приложение simple-flow
демонстрирует самые базовые строительные блоки приложения Faces Flows и иллюстрирует некоторые соглашения, облегчающие начало работы с итеративной разработкой с использованием Flow. Вы можете начать с простого примера, подобного этому, и продолжить его.
В этом примере приводится неявное определение Flow путём включения пустого файла конфигурации. Файл конфигурации с содержимым или аннотированный класс @FlowDefinition
предоставляет явное определение Flow.
Исходный код для этого приложения находится в каталоге tut-install/examples/web/jsf/simple-flow/
.
Расположение файла в примере simple-flow
выглядит следующим образом:
src/main/webapp
index.xhtml
simple-flow-return.xhtml
WEB_INF/
web.xml
simple-flow
simple-flow-flow.xml
simple-flow.xhtml
simple-flow-page2.xhtml
В примере simple-flow
есть пустой файл конфигурации, который по соглашению называется flow-name-flow.xml
. Flow не требует какой-либо настройки по следующим причинам.
-
Flow не вызывает другой Flow и не передаёт параметры другому Flow.
-
Flow использует имена по умолчанию для первой страницы Flow, flow-name`.xhtml`, и страницу возврата, flow-name`-return.xhtml`.
В этом примере всего четыре страницы Facelets.
-
index.xhtml
— стартовая страница, которая почти ничего не содержит, кроме кнопки, перемещающей на первую страницу Flow:
<p><h:commandButton value="Enter Flow" action="simple-flow"/></p>
-
simple-flow.xhtml
и simple-flow-page2.xhtml
— две страницы самого Flow. При отсутствии явного определения Flow страница, имя которой совпадает с именем Flow, считается начальным узлом Flow. В этом случае Flow называется simple-flow
, поэтому предполагается, что начальным узлом Flow является страница simple-flow.xhtml
. Начальный узел — это узел, к которому переходят при входе в Flow. Его можно рассматривать как домашнюю страницу Flow.
Страница simple-flow.xhtml
попросит вас ввести значение в области Flow и предоставит кнопку для перехода на следующую страницу Flow:
<p>Value: <h:inputText id="input" value="#{flowScope.value}" /></p>
<p><h:commandButton value="Next" action="simple-flow-page2" /></p>
Вторая страница, которая может иметь любое имя, отображает значение в области видимости Flow и предоставляет кнопку для перехода на страницу возврата:
<p>Value: #{flowScope.value}</p>
<p><h:commandButton value="Return" action="simple-flow-return" /></p>
-
simple-flow-return.xhtml
— страница возврата. Страница возврата, которая по соглашению называется flow-name`-return.xhtml`, должна находиться за пределами Flow. На этой странице отображается значение области видимости Flow, показывающее, что оно не имеет значения вне Flow, и предоставляется ссылка, которая ведет на страницу index.xhtml
:
<p>Value (should be empty):
"<h:outputText id="output" value="#{flowScope.value}" />"</p>
<p><h:link outcome="index" value="Back to Start" /></p>
Страницы Facelets используют только данные области видимости Flow, поэтому в примере не требуется Managed-бин.
Сборка, упаковка и развёртывание simple-flow в IDE NetBeans
-
Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).
-
В меню «Файл» выберите «Открыть проект».
-
В диалоговом окне «Открыть проект» перейдите к:
tut-install/examples/web/jsf
-
Выберите каталог simple-flow
.
-
Нажмите Открыть проект.
-
На вкладке «Проекты» кликните правой кнопкой мыши проект simple-flow
и выберите «Сборка».
Эта команда собирает и упаковывает приложение в WAR-файл, simple-flow.war
, который находится в каталоге target
. Затем приложение развёртывается на сервере.
Сборка, упаковка и развёртывание simple-flow с использованием Maven
-
Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).
-
В окне терминала перейдите в:
tut-install/examples/web/jsf/simple-flow/
-
Введите следующую команду:
Эта команда собирает и упаковывает приложение в WAR-файл, simple-flow.war
, который находится в каталоге target
. Затем приложение развёртывается на сервере.
Запуск simple-flow
-
Введите следующий URL в браузере:
http://localhost:8080/simple-flow
-
На странице index.xhtml
нажмите Enter Flow.
-
На первой странице Flow введите любую строку в поле «Значение», затем нажмите «Далее».
-
На второй странице Flow вы можете увидеть введённое вами значение. Нажмите Return.
-
На странице возврата пустая пара кавычек заключает в себе недоступное значение. Нажмите кнопку «Назад в начало», чтобы вернуться на страницу index.xhtml
.
Приложение checkout-module
Приложение checkout-module
значительно сложнее, чем simple-flow
. Он показывает, как вы можете использовать функцию Faces Flows для реализации модуля оформления заказа для интернет-магазина.
Как и гипотетический пример на рис. 16-1, пример приложения содержит два Flow, каждый из которых может вызывать другой. Оба Flow имеют явные определения. Один Flow, checkoutFlow
, определяется программно. Другой Flow, joinFlow
, указан в файле конфигурации.
Исходный код для этого приложения находится в каталоге tut-install/examples/web/jsf/checkout-module/
.
Для приложения checkout-module
структура каталогов следующая (есть также каталог src/main/webapp/resources
с таблицей стилей и изображением):
src/main/webapp/
index.xhtml
exithome.xhtml
WEB_INF/
beans.xml
web.xml
checkoutFlow/
checkoutFlow.xhtml
checkoutFlow2.xhtml
checkoutFlow3.xhtml
checkoutFlow4.xhtml
joinFlow/
joinFlow-flow.xml
joinFlow.xhtml
joinFlow2.xhtml
src/main/java/javaeetutorial/checkoutmodule
CheckoutBean.java
CheckoutFlow.java
CheckoutFlowBean.java
JoinFlowBean.java
Например, index.xhtml
является начальной страницей для приложения, а также узлом возврата для Flow оформления заказа. Страница exithome.xhtml
является узлом возврата для Flow объединения.
Файл конфигурации joinFlow-flow.xml
определяет Flow объединения, а исходный файл CheckoutFlow.java
определяет Flow оформления.
Flow оформления содержит четыре страницы Facelets, а Flow объединения — две.
Managed-бины, охватываемые каждым Flow: CheckoutFlowBean.java
и JoinFlowBean.java
, тогда как CheckoutBean.java
является вспомогательным бином для страницы index.html
.
Страницы Facelets для checkout-module
Начальная страница для примера index.xhtml
отображает содержимое гипотетической корзины покупок. Это позволяет пользователю нажать любую из двух кнопок, чтобы войти в один из двух Flow:
<p><h:commandButton value="Check Out" action="checkoutFlow"/></p>
...
<p><h:commandButton value="Join" action="joinFlow"/></p>
Эта страница также является возвратным узлом для Flow оформления заказа.
Страница Facelets exithome.xhtml
является узлом возврата для Flow объединения. На этой странице есть кнопка, позволяющая вернуться на страницу index.xhtml
.
Четыре страницы Facelets во Flow оформления заказа, начиная с checkoutFlow.xhtml
и заканчивая checkoutFlow4.xhtml
, позволяют перейти на следующую страницу или, в некоторых случаях, вернуться из Flow. Страница checkoutFlow.xhtml
позволяет получить доступ к параметрам, переданным из Flow объединения через область видимости Flow. Они отображаются в виде пустых кавычек, если вы не вызвали Flow оформления из Flow объединения.
<p>If you called this flow from the Join flow, you can see these parameters:
"<h:outputText value="#{flowScope.param1Value}"/>" and
"<h:outputText value="#{flowScope.param2Value}"/>"
</p>
Только checkoutFlow2.xhtml
имеет кнопку для возврата на предыдущую страницу, но перемещение между страницами обычно разрешено во Flow. Вот кнопки для checkoutFlow2.xhtml
:
<p><h:commandButton value="Continue" action="checkoutFlow3"/></p>
<p><h:commandButton value="Go Back" action="checkoutFlow"/></p>
<p><h:commandButton value="Exit Flow" action="returnFromCheckoutFlow"/></p>
Действие returnFromCheckoutFlow
определено в файле исходного кода CheckoutFlow.java
.
Последняя страница Flow оформления checkoutFlow4.xhtml
содержит кнопку, которая вызывает Flow объединения:
<p><h:commandButton value="Join" action="calljoin"/></p>
<p><h:commandButton value="Exit Flow" action="returnFromCheckoutFlow"/></p>
Действие calljoin
также определено в файле исходного кода конфигурации, CheckoutFlow.java
. Это действие входит во Flow объединения, получая два параметра из Flow оформления.
Две страницы во Flow объединения, joinFlow.xhtml
и joinFlow2.xhtml
, аналогичны тем, которые находятся во Flow оформления. На второй странице есть кнопка для вызова Flow оформления заказа и кнопка для возврата из Flow объединения:
<p><h:commandButton value="Check Out" action="callcheckoutFlow"/></p>
<p><h:commandButton value="Exit Flow" action="returnFromJoinFlow"/></p>
Для этого Flow действия callcheckoutFlow
и returnFromJoinFlow
определены в файле конфигурации joinFlow-flow.xml
.
Если вы используете файл конфигурации приложения для настройки Flow, он должен называться flowName`-flow.xml`. В этом примере Flow объединения использует файл конфигурации с именем joinFlow-flow.xml
. Этот файл является файлом faces-config
, в котором указан элемент flow-definition
. Этот элемент должен определять имя Flow, используя атрибут id
. Под элементом flow-definition
должен быть элемент flow-return
, который указывает точку возврата для Flow. Все входные параметры указываются с помощью элементов inbound-parameter
. Если Flow вызывает другой Flow, элемент call-flow
должен использовать элемент flow-reference для именования вызываемого Flow и может использовать элемент outbound-parameter
для указания любого исходящего параметры.
Файл конфигурации для Flow объединения выглядит следующим образом:
<faces-config version="2.2" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee \
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<flow-definition id="joinFlow">
<flow-return id="returnFromJoinFlow">
<from-outcome>#{joinFlowBean.returnValue}</from-outcome>
</flow-return>
<inbound-parameter>
<name>param1FromCheckoutFlow</name>
<value>#{flowScope.param1Value}</value>
</inbound-parameter>
<inbound-parameter>
<name>param2FromCheckoutFlow</name>
<value>#{flowScope.param2Value}</value>
</inbound-parameter>
<flow-call id="callcheckoutFlow">
<flow-reference>
<flow-id>checkoutFlow</flow-id>
</flow-reference>
<outbound-parameter>
<name>param1FromJoinFlow</name>
<value>param1 joinFlow value</value>
</outbound-parameter>
<outbound-parameter>
<name>param2FromJoinFlow</name>
<value>param2 joinFlow value</value>
</outbound-parameter>
</flow-call>
</flow-definition>
</faces-config>
Атрибут id
элемента flow-definition
определяет имя Flow как joinFlow
. Значение атрибута id
элемента flow-return
идентифицирует имя возвращаемого узла, а его значение определяется в from-outcome
элемент как свойство returnValue
Managed-бина области видимости Flow для Flow объединения JoinFlowBean
.
Имена и значения входных параметров извлекаются из области видимости Flow в следующем порядке (flowScope.param1Value
, flowScope.param2Value
), согласно тому, как это было определено в и конфигурации Flow оформления.
Элемент flow-call
определяет, как Flow объединения вызывает Flow оформления. Атрибут id
элемента callcheckoutFlow
определяет действие вызова Flow. В элементе flow-call
элемент flow-reference
определяет фактическое имя вызываемого Flow, checkoutFlow
. Элементы outbound-parameter
определяют параметры, которые будут переданы при вызове checkoutFlow
. Здесь это просто произвольные строки.
Если вы используете класс Java для настройки Flow, он должен совпадать с Flow по имени. Класс для Flow оформления называется CheckoutFlow.java
.
import java.io.Serializable;
import javax.enterprise.inject.Produces;
import javax.faces.flow.Flow;
import javax.faces.flow.builder.FlowBuilder;
import javax.faces.flow.builder.FlowBuilderParameter;
import javax.faces.flow.builder.FlowDefinition;
class CheckoutFlow implements Serializable {
private static final long serialVersionUID = 1L;
@Produces
@FlowDefinition
public Flow defineFlow(@FlowBuilderParameter FlowBuilder flowBuilder) {
String flowId = "checkoutFlow";
flowBuilder.id("", flowId);
flowBuilder.viewNode(flowId,
"/" + flowId + "/" + flowId + ".xhtml").
markAsStartNode();
flowBuilder.returnNode("returnFromCheckoutFlow").
fromOutcome("#{checkoutFlowBean.returnValue}");
flowBuilder.inboundParameter("param1FromJoinFlow",
"#{flowScope.param1Value}");
flowBuilder.inboundParameter("param2FromJoinFlow",
"#{flowScope.param2Value}");
flowBuilder.flowCallNode("calljoin").flowReference("", "joinFlow").
outboundParameter("param1FromCheckoutFlow",
"#{checkoutFlowBean.name}").
outboundParameter("param2FromCheckoutFlow",
"#{checkoutFlowBean.city}");
return flowBuilder.getFlow();
}
}
Класс выполняет действия, практически идентичные тем, которые выполняются файлом конфигурации joinFlow-flow.xml
. Он содержит единственный метод defineFlow
в качестве метода-производителя с квалификатором @FlowDefinition
, который возвращает класс javax.faces.flow.Flow
. Метод defineFlow
принимает один параметр — FlowBuilder
с квалификатором @FlowBuilderParameter
, который передаётся из JavaServer Faces. Затем метод вызывает методы класса javax.faces.flow.Builder.FlowBuilder
для настройки Flow.
Во-первых, метод определяет id
Flow как checkoutFlow
. Затем он явно определяет начальный узел для Flow. По умолчанию это имя Flow с суффиксом .xhtml
.
Затем метод определяет возвратный узел аналогично тому, как это делает файл конфигурации. Метод returnNode
устанавливает имя возвращаемого узла как returnFromCheckoutFlow
, а метод fromOutcome
указывает его значение как свойство returnValue
Managed-бина во Flow оформления, CheckoutFlowBean
.
Метод inboundParameter
устанавливает имена и значения входных параметров из Flow объединения, которые извлекаются из области видимости Flow в следующем порядке (flowScope.param1Value
, flowScope. param2Value
), согласно тому, как определено в в файле конфигурации Flow объединения.
Метод flowCallNode
определяет, как Flow оформления вызывает Flow объединения. Аргумент calljoin
определяет действие вызова Flow. Связанный метод flowReference
определяет фактическое имя вызываемого Flow, joinFlow
, а затем вызывает методы outboundParameter
для определения параметров, передаваемых при вызове joinFlow
. Здесь это значения из Managed-бина CheckoutFlowBean
.
Наконец, метод defineFlow
вызывает метод getFlow
и возвращает результат.
Область видимости Flow для Managed-бина
Каждый из двух Flow имеет Managed-бин, который определяет свойства страниц в Flow. Например, CheckoutFlowBean
определяет свойства, значения которых вводятся пользователем как на странице checkoutFlow.xhtml
, так и на странице checkoutFlow3.xhtml
.
Каждый Managed-бин имеет метод getReturnValue
, который устанавливает значение возвращаемого узла. Для CheckoutFlowBean
возвратным узлом является страница index.xhtml
, указанная с помощью неявной навигации:
public String getReturnValue() {
return "index";
}
Для JoinFlowBean
возвратным узлом является страница exithome.xhtml
.
Сборка, упаковка и развёртывание checkout-module в IDE NetBeans
-
Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).
-
В меню «Файл» выберите «Открыть проект».
-
В диалоговом окне «Открыть проект» перейдите к:
tut-install/examples/web/jsf
-
Выберите каталог checkout-module
.
-
Нажмите Открыть проект.
-
На вкладке «Проекты» кликните правой кнопкой мыши проект checkout-module
и выберите «Сборка».
Эта команда собирает и упаковывает приложение в WAR-файл, checkout-module.war
, который находится в каталоге target
. Затем приложение развёртывается на сервере.
Сборка, упаковка и развёртывание checkout-module с помощью Maven
-
Удостоверьтесь, чтобы GlassFish Server был запущен (см. Запуск и остановка сервера GlassFish).
-
В окне терминала перейдите в:
tut-install/examples/web/jsf/checkout-module/
-
Введите следующую команду:
Эта команда собирает и упаковывает приложение в WAR-файл, checkout-module.war
, который находится в каталоге target
. Затем приложение развёртывается на сервере.
Запуск checkout-module
-
Введите следующий URL в браузере:
http://localhost:8080/checkout-module
-
На странице index.xhtml
представлены гипотетические результаты похода за покупками. Нажмите «Check Out» или «Join», чтобы войти в один из двух Flow.
-
Следуйте за Flow, предоставляя входные данные по мере необходимости и выбирая, продолжить, вернуться назад или выйти из Flow.
Во Flow оформления заказа проверяется только одно из полей ввода (поле кредитной карты рассчитано на 16 цифр), поэтому вы можете ввести любые значения, которые вам нравятся. Flow объединения не требует от вас отмечать какие-либо флажки в меню флажков.
-
На последней странице Flow выберите опцию для ввода другого Flow. Это позволяет просматривать входные параметры из предыдущего Flow.
-
Поскольку Flow являются вложенными, если вы нажмёте Exit Flow из вызываемого Flow, вы вернётесь к первой странице вызывающего Flow. (Возможно появится предупреждение, которое можно игнорировать.) Нажмите «Выход из Flow» на этой странице, чтобы перейти к указанному возвратному узлу.