Table of Contents

Назад к FAQ

JSF Request Lifecycle

Big Picture

Описание стадий

Restore View

На стадии Restore View реализация JSF должна выполнить следующие действия:

В конце этой стадии свойство viewRoot у FacesContext'а для текущего запроса будет содержать сохранённое состояние вида, которое было в предыдущем запросе или новый вид, созданный с помощью ViewHandler#createView() для указанного идентификатора вида.

Apply Request Values

Назначение этой стадии - дать каждому компоненту возможность обновить своё состояние на основании информации в текущем запросе (параметры, заголовки, куки и т.д.).

В течение этой стадии реализация JSF должна вызвать метод UIViewRoot#processDecodes. По-нормальному это повлечёт за собой вызовы метода UIComponent#processDecodes для всех компонентов в дереве компонентов, как описано в явадоке к методу UIComponent.processDecodes(). Для UIInput-компонентов, преобразование данных должно быть проведено в соответствии с явадоком к UIInput.

В процессе декодирования данных из запроса, компоненты могут производить хитрые действия, включающие в себя:

В конце этой фазы все компоненты, которые реализуют интерфейс EditableValueHolder, будут обновлены в соответствии с данными, переданными в запросе (или с достаточным количеством данных чтобы вопроизвести некорректный ввод, если были ошибки преобразования). В дополнение к этому, для этих компонентов, у которых свойство immediate выставлено в true, будут выполнены преобразование и валидация. Преобразования и валидации, которые не удались, с помощью метода FacesContext#addMessage могут сигнализировать об ошибках для соответствующих компонентов и их свойство valid будет выставлено в false5).

Если какой-либо из вызванных методов decode() или слушатель событий (event listener), который обрабатывал событие, вызвал метод FacesContext#responseComplete(), то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов decode() или слушатель событий (event listener), который обрабатывал событие, вызвал метод FacesContext#renderResponse(), то управление должно немедленно перейти к фазе Render Response. Иначе управление должно перейти к фазе Process Validations.

Process Validations

В процессе создания вида (view) для текущего запроса, к компонентам могут быть добавлены валидаторы. Помимо этого, компоненты могут сами реализовывать логику валидации в их методах validate()6).

На стадии Process Validations жизненного цикла запроса реализация JSF должна вызвать метод UIViewRoot#processValidators(). По-нормальному это повлечёт за собой рекурсивный вызов метода UIComponent#processValidators() для каждого компонента в дереве компонентов, как написано в документации к этому методу. Обратите внимание, что компоненты, реализующие EditableValueHolder, у которых свойство immediate выставлено в true, уже выполнили свою валидацию на стадии Apply Request Values.

В процессе валидации, события могут добавляться в очередь компонентами и/или валидаторами, у которых вызывается метод validate(). После этого они (события) будут распространены заинтересованным слушателям событий.

В конце фазы все преобразования и настроенные валидации будут выполнены. Те преобразования и валидации, которые провалились7), сигнализируют об этом посредством добавления сообщений вызывая метод FacesContext#addMessage() у контекста для текущего запроса, и их свойство valid выставлено в false8).

Если какой-либо из вызванных методов validate() или слушатель событий (event listener), который обрабатывал событие, вызвал метод FacesContext#responseComplete(), то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов validate() или слушатель событий (event listener), который обрабатывал событие, вызвал метод FacesContext#renderResponse(), то управление должно немедленно перейти к фазе Render Response. Иначе управление должно перейти к фазе Update Model Values.

Update Model Values

Если обрабока запроса дошла до этой стадии, то предполагается, что запрос синтаксически и семантически9) корректен, что локальное значение каждого компонента в дереве компонентов было обновлено, и что теперь можно обновлять данные в модели приложения для подготовки к выполнению событий приложения, которые находятся в очереди.

В течение фазы Update Model Values, реализация JSF должна вызвать метод UIViewRoot#processUpdates(). По-нормальному это повлечёт за собой вызов метода UIComponent#processUpdates() для каждого компонента в дереве, как описано в документации к этому методу. Обновление модели выполняется в методе updateModel()10) компонента.

В процессе обновления модели, события могут добавляться в очередь компонентами, у которых вызывается метод updateModel(). После этого они (события) будут распространены заинтересованным слушателям событий. В конце этой фазы у всех подходящих11) объектов данных будут обновлены их значения до значений, содержащихся в соответствующих компонентах, а локальные значения в компонентах будут очищены.

Если какой-либо из вызванных методов updateModel() или слушатель событий (event listener), который обрабатывал событие, вызвал метод FacesContext#responseComplete(), то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов updateModel() или слушатель событий (event listener), который обрабатывал событие, вызвал метод FacesContext#renderResponse(), то управление должно немедленно перейти к фазе Render Response. Иначе управление должно перейти к фазе Invoke Application.

Invoke Application

Если обрабока запроса дошла до этой стадии, то предполагается, что все обновления модели выполнены и оставшиеся события должны быть переданы приложению. Реализация JSF должна убедиться, что вызван метод UIViewRoot#processApplication(). По умолчанию этот метод распространяет все события, у которых задан идентификатор фазы PhaseId.INVOKE_APPLICATION.

Продвинутые приложения (или фрэймворки) могут заменять ActionListener, вызвав метод Application#setActionListener() для текущего приложения. Однако реализация JSF должна предоставить ActionListener по умолчанию, который ведёт себя согласно описанию.

Render Response

Эта фаза производит два действия:

  1. Выводит результат клиенту
  2. Сохраняет состояние результата для последующий запросов.

Эти две задачи объединены в одну фазу потому, что в JSP-приложениях вывод результата может вызвать построение вида (view) по мере того, как выводится страница. Поэтому невозможно сохранить состояние до тех пор пока вид не построен, а состояние необходимо сохранить дотого, как вывод будет отправлен клиенту для того, чтобы сохранить у клиента состояние.

JSF поддерживает различные подходы, которые реализации JSF могут использовать при создании результата, который соответствует содержимому результирующего вида, включая:

Из-за большого количества возможных вариантов, механизм реализации фазы Render Response не может быть точно описан. Однако, все реализации этой фазы должны удовлетворять следующим требованиям:

Для компонентов, которые реализуют интерфейс ValueHolder (например, UIInput и UIOutput), должно выполняться преобразование данных в соответствии с тем, как это описано в документации к UIOutput.

По завершении рендеринга, конечное состояние вида должно быть сохранено посредством методов класса StateManager. Эта информация о состоянии должна быть доступна при последующем запросе, чтобы ей можно было воспользоваться в фазе Restore View. Более подробная информацию о StateManager'е можно найти здесь.

На практике

Делаем простенький PhaseListener:

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
 
public class MyPhaseListener implements PhaseListener {
  public void afterPhase(PhaseEvent event) {
  }
 
  public void beforePhase(PhaseEvent event) {
    if (event.getPhaseId().equals(PhaseId.RESTORE_VIEW)) {
      System.err.println("----");
    }
    System.err.println(event.getPhaseId().toString());
  }
 
  public PhaseId getPhaseId() {
    return PhaseId.ANY_PHASE;
  }
}

И наблюдаем следующую картину:

RESTORE_VIEW 1
RENDER_RESPONSE 6

Вот то самое, о чём я говорил чуть выше.

RESTORE_VIEW 1
APPLY_REQUEST_VALUES 2
PROCESS_VALIDATIONS 3
UPDATE_MODEL_VALUES 4
INVOKE_APPLICATION 5
RESTORE_VIEW 1
RENDER_RESPONSE 6

Дамы и господа, обратите внимание. Запроса два. Причём оба какие-то неполноценные. В первом нет фазы RENDER_RESPONSE, во втором всё как при простом обращении к странице.

Вывод: если вы хотите, чтобы какой-то кусок кода вызывался при каждом обращении к JSF-приложению (например, вы хотите защитить какие-то страницы от просмотра, то необходимо либо писать фильтр, чтобы запросы к JSF-сервлету проходили сначала через него, либо писать PhaseListener, который будет висеть на фазе RESTORE_VIEW.

Ссылки по теме

1)
По-английски это называется extra path information of the request URI, но ХЗ как это перевести на русский.
2)
Объясните мне, что это за фигня.
3)
Кажется я понял. Это тот суффикс, который задаётся в web.xml с помощью
<context-param>
    <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
    <param-value>.jspx</param-value>
  </context-param>
4)
Какое - непонятно. Видимо любое.
5)
Вопрос: кем выставлено? Самим компонентом или реализацией JSF?
6) , 10)
Найти явадок.
7)
FIXME В оригинале failed. Заменить на более русское слово.
8)
Опять же, кем? Реализацией JSF или самим компонентом?
9)
Не спрашивайте меня, что это означает.
11)
В оригинале appropriate.
12)
Обычно порядок задаётся специальной разметкой (например, JSP-тэгами) в шаблоне, ассоциированном с деревом компонентов.
13)
Например, эта техника используется когда custom-тэги в JSP страницах используются в качестве технологии для рендеринга, как описано в Интеграции с JSP
14)
Вот этот момент я плохо понял.