User Tools

Site Tools


jsf-ru:faq:request_lifecycle

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
jsf-ru:faq:request_lifecycle [2006/07/08 20:13] slonopotamusjsf-ru:faq:request_lifecycle [2019/06/12 16:08] (current) – external edit 127.0.0.1
Line 1: Line 1:
 +[[jsf-ru:faq|Назад к FAQ]]
 +====== JSF Request Lifecycle ======
 +
 +
 +
 +===== Big Picture =====
 +{{jsf-ru:faq:jsf-lifecycle.gif}}
 +
 +===== Описание стадий =====
 +
 +
 +
 +==== Restore View ====
 +На стадии //Restore View// реализация JSF должна выполнить следующие действия:
 +  * Проверить FacesContext для текущего запроса. Если он содержит UIViewRoot, то
 +    * Выставить ''UIViewRoot'''у локаль, полученную из метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/ExternalContext.html#getRequestLocale()|ExternalContext.html#getRequestLocale()]] для текущего запроса.
 +    * FIXME For each component in the component tree, determine if a ''ValueBinding'' for "binding" is present. If so, call the setValue() method on this ValueBinding, passing the component instance on which it was found.
 +    * Не предпринимать дальнейших действий на этой стадии.
 +  * Определить идентификатор вида (//view identifier//) для текущего запроса:
 +    * Если для ''FacesServlet'''а используется маппинг с префиксом (например, "''/faces/*''"), то в качестве ''viewId'' использовать то, что на месте ''*''((По-английски это называется //extra path information of the request URI//, но ХЗ как это перевести на русский.)).
 +    * Если используется маппинг с суффиксом (например, "''*.faces''"), то в качестве ''viewId'' берётся путь, по которому был сделан запрос, в котором суффикс заменяется на указанный в параметре инициализации контекста с именем равным //символической константе//((Объясните мне, что это за фигня.)) ''ViewHandler.DEFAULT_SUFFIX_NAME'' (если этот параметр не присутствует, использовать значение символической константы ''ViewHandler.DEFAULT_SUFFIX'' в качестве суффикса для замены.((Кажется я понял. Это тот суффикс, который задаётся в ''web.xml'' с помощью <code xml><context-param>
 +    <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
 +    <param-value>.jspx</param-value>
 +  </context-param></code>))
 +    * Если идентификатор вида определить не удаётся, бросить исключение((Какое - непонятно. Видимо любое.)).
 +  * Вызвать метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/ViewHandler.html#restoreView(javax.faces.context.FacesContext, java.lang.String)|ViewHandler#restoreView()]], передав ему полученный ''FacesContext'' и идентификатор вида в качестве аргументов и получив (возможно) ''UIViewRoot'' в качестве результата.
 +    * Если ''restoreView()'' вернул ''null'', то вызвать [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/ViewHandler.html#createView(javax.faces.context.FacesContext, java.lang.String)|ViewHandler#createView()]] и затем [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]].
 +    * Если запрос не содержит ''POST''-данных или параметров в запросе (//query parameters//), то вызвать [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]].
 +  * Сохранить созданный или восстановленный ''UIViewRoot'' в ''FacesContext'''е.
 +  * FIXME For each component in the component tree, determine if a ''ValueBinding'' for "binding" is present. If so, call the setValue() method on this ValueBinding, passing the component instance on which it was found.
 +
 +В конце этой стадии свойство ''viewRoot'' у ''FacesContext'''а для текущего запроса будет содержать сохранённое состояние вида, которое было в предыдущем запросе или новый вид, созданный с помощью [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/ViewHandler.html#createView(javax.faces.context.FacesContext, java.lang.String)|ViewHandler#createView()]] для указанного идентификатора вида.
 +
 +
 +
 +==== Apply Request Values ====
 +Назначение этой стадии - дать каждому компоненту возможность обновить своё состояние на основании информации в текущем запросе (параметры, заголовки, куки и т.д.).
 +
 +В течение этой стадии реализация JSF должна вызвать метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIViewRoot.html#processDecodes(javax.faces.context.FacesContext)|UIViewRoot#processDecodes]]. По-нормальному это повлечёт за собой вызовы метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIComponent.html#processDecodes(javax.faces.context.FacesContext)|UIComponent#processDecodes]] для всех компонентов в дереве компонентов, как описано в явадоке к методу ''UIComponent.processDecodes()''. Для ''UIInput''-компонентов, преобразование данных должно быть проведено в соответствии с явадоком к [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIInput.html|UIInput]].
 +
 +В процессе декодирования данных из запроса, компоненты могут производить хитрые действия, включающие в себя:
 +  * Компоненты, которые реализуют интерфейс [http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/ActionSource.html|ActionSource]] (например, [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UICommand.html|UICommand]]), которые определят, что они были активированы, добавят [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/event/ActionEvent.html|ActionEvent]] в очередь событий. Это событие будет вызвано в конце фазы [[#apply.request.values|Apply Request Values]] или в конце фазы [[#invoke.application|Invoke Application]], в зависимости от состояния свойства ''immediate'' у активированного компонента.
 +  * Компоненты, которые реализуют интерфейс [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/EditableValueHolder.html|EditableValueHolder]] (например, [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIInput.html|UIInput]]), у которых свойство ''immediate'' выставлено в ''true'', вызовут преобразование и валидацию (включая потенциально запуск событий [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/event/ValueChangeEvent.html|ValueChangeEvent]], которые по-нормальному происходят в фазе [[#process.validations|Process Validations]], вместо этого будут вызваны в фазе [[#apply.request.values|Apply Request Values]].
 +
 +В конце этой фазы все компоненты, которые реализуют интерфейс [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/EditableValueHolder.html|EditableValueHolder]], будут обновлены в соответствии с данными, переданными в запросе (или с достаточным количеством данных чтобы вопроизвести некорректный ввод, если были ошибки преобразования). В дополнение к этому, для этих компонентов, у которых свойство ''immediate'' выставлено в ''true'', будут выполнены преобразование и валидация. Преобразования и валидации, которые не удались, с помощью метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#addMessage(java.lang.String, javax.faces.application.FacesMessage)|FacesContext#addMessage]] могут сигнализировать об ошибках для соответствующих компонентов и их свойство ''valid'' будет выставлено в ''false''((Вопрос: кем выставлено? Самим компонентом или реализацией JSF?)).
 +
 +Если какой-либо из вызванных методов ''decode()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#responseComplete()|FacesContext#responseComplete()]], то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов ''decode()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]], то управление должно немедленно перейти к фазе [[#render.response|Render Response]]. Иначе управление должно перейти к фазе [[#process.validations|Process Validations]].
 +
 +
 +
 +
 +==== Process Validations ====
 +В процессе создания вида (''view'') для текущего запроса, к компонентам могут быть добавлены [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/validator/Validator.html|валидаторы]]. Помимо этого, компоненты могут сами реализовывать логику валидации в их методах ''validate()''((Найти явадок.)).
 +
 +На стадии [[#process.validations|Process Validations]] жизненного цикла запроса реализация JSF должна вызвать метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIViewRoot.html#processValidators(javax.faces.context.FacesContext)|UIViewRoot#processValidators()]]. По-нормальному это повлечёт за собой рекурсивный вызов метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIComponent.html#processValidators(javax.faces.context.FacesContext)|UIComponent#processValidators()]] для каждого компонента в дереве компонентов, как написано в документации к этому методу. Обратите внимание, что компоненты, реализующие [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/EditableValueHolder.html|EditableValueHolder]], у которых свойство ''immediate'' выставлено в ''true'', уже выполнили свою валидацию на стадии [[#apply.request.values|Apply Request Values]].
 +
 +В процессе валидации, события могут добавляться в очередь компонентами и/или валидаторами, у которых вызывается метод ''validate()''. После этого они (события) будут распространены заинтересованным слушателям событий.
 +
 +В конце фазы все преобразования и настроенные валидации будут выполнены. Те преобразования и валидации, которые провалились((FIXME В оригинале ''failed''. Заменить на более русское слово.)), сигнализируют об этом посредством добавления сообщений вызывая метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#addMessage(java.lang.String, javax.faces.application.FacesMessage)|FacesContext#addMessage()]] у контекста для текущего запроса, и их свойство ''valid'' выставлено в ''false''((Опять же, кем? Реализацией JSF или самим компонентом?)).
 +
 +Если какой-либо из вызванных методов ''validate()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#responseComplete()|FacesContext#responseComplete()]], то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов ''validate()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]], то управление должно немедленно перейти к фазе [[#render.response|Render Response]]. Иначе управление должно перейти к фазе [[#update.model.values|Update Model Values]].
 +
 +
 +==== Update Model Values ====
 +Если обрабока запроса дошла до этой стадии, то предполагается, что запрос синтаксически и семантически((Не спрашивайте меня, что это означает.)) корректен, что локальное значение каждого компонента в дереве компонентов было обновлено, и что теперь можно обновлять данные в модели приложения для подготовки к выполнению событий приложения, которые находятся в очереди.
 +
 +В течение фазы [[#update.model.values|Update Model Values]], реализация JSF должна вызвать метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIViewRoot.html#processUpdates(javax.faces.context.FacesContext)|UIViewRoot#processUpdates()]]. По-нормальному это повлечёт за собой вызов метода [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIComponent.html#processUpdates(javax.faces.context.FacesContext)|UIComponent#processUpdates()]] для каждого компонента в дереве, как описано в документации к этому методу. Обновление модели выполняется в методе ''updateModel()''((Найти явадок.)) компонента.
 +
 +В процессе обновления модели, события могут добавляться в очередь компонентами, у которых вызывается метод ''updateModel()''. После этого они (события) будут распространены заинтересованным слушателям событий. В конце этой фазы у всех подходящих((В оригинале ''appropriate''.)) объектов данных будут обновлены их значения до значений, содержащихся в соответствующих компонентах, а локальные значения в компонентах будут очищены.
 +
 +Если какой-либо из вызванных методов ''updateModel()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#responseComplete()|FacesContext#responseComplete()]], то обработка текущего запроса должна быть немедленно прекращена. Если какой-либо из вызванных методов ''updateModel()'' или слушатель событий (//event listener//), который обрабатывал событие, вызвал метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/context/FacesContext.html#renderResponse()|FacesContext#renderResponse()]], то управление должно немедленно перейти к фазе [[#render.response|Render Response]]. Иначе управление должно перейти к фазе [[#invoke.application|Invoke Application]].
 +
 +
 +==== Invoke Application ====
 +Если обрабока запроса дошла до этой стадии, то предполагается, что все обновления модели выполнены и оставшиеся события должны быть переданы приложению. Реализация JSF должна убедиться, что вызван метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIViewRoot.html#processApplication(javax.faces.context.FacesContext)|UIViewRoot#processApplication()]]. По умолчанию этот метод распространяет все события, у которых задан идентификатор фазы [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/event/PhaseId.html#INVOKE_APPLICATION|PhaseId.INVOKE_APPLICATION]].
 +
 +Продвинутые приложения (или фрэймворки) могут заменять ''ActionListener'', вызвав метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/Application.html#setActionListener(javax.faces.event.ActionListener)|Application#setActionListener()]] для текущего приложения. Однако реализация JSF должна предоставить ''ActionListener'' по умолчанию, который ведёт себя согласно [[ActionListener|описанию]].
 +
 +
 +
 +==== Render Response ====
 +Эта фаза производит два действия:
 +  - Выводит результат клиенту
 +  - Сохраняет состояние результата для последующий запросов.
 +
 +Эти две задачи объединены в одну фазу потому, что в JSP-приложениях вывод результата может вызвать построение вида (''view'') по мере того, как выводится страница. Поэтому невозможно сохранить состояние до тех пор пока вид не построен, а состояние необходимо сохранить дотого, как вывод будет отправлен клиенту для того, чтобы сохранить у клиента состояние.
 +
 +JSF поддерживает различные подходы, которые реализации JSF могут использовать при создании результата, который соответствует содержимому результирующего вида, включая:
 +  * Получение всего вывода напрямую из результатов вызова методов ''encode()'' (либо компонентов либо соответствующих рендереров).
 +  * Чередование результатов энкодинга компонентов с содержимым которое динамически генерируется логикой приложения.
 +  * Чередование результатов энкодинга компонентов с контентом, который копируется из статического "шаблонного" ресурса.
 +  * Использование результатов энкодинга компонентов посредством вызовов методов в динамическом ресурсе (именно так отображаются компоненты в виде custom-тэгов в JSP-страницах).
 +
 +Из-за большого количества возможных вариантов, механизм реализации фазы [[#render.response|Render Response]] не может быть точно описан. Однако, все реализации этой фазы должны удовлетворять следующим требованиям:
 +  * Реализация JSF должна предоставить реализацию ''ViewHandler'''а по умолчанию, которая вызывает метод [[http://java.sun.com/j2ee/1.4/docs/api/javax/servlet/RequestDispatcher.html#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)|RequestDispatcher#forward()]] для ресурса, путь (относительно контекста) которого равен идентификатору вида текущего дерева компонентов.
 +  * Если весь вывод получается из энкодирующих методов компонентов или ассоциированных с ними рендереров, то дерево компонентов должно обходиться в таком же порядке, в каком оно обходилось на более ранних фазах.
 +  * Если содержимое вывода получается из энкодирующих методов и дополнительных источников, компоненты могут рендериться (мда) в произвольном порядке((Обычно порядок задаётся специальной разметкой (например, JSP-тэгами) в шаблоне, ассоциированном с деревом компонентов.)).
 +  * В процессе рендеринга дополнительные компоненты могут быть добавлены в дерево компонентов на основании информации, имеющейся у реализации [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/ViewHandler.html|ViewHandler]]'а((Например, эта техника используется когда custom-тэги в JSP страницах используются в качестве технологии для рендеринга, как описано в [[jsp_integration|Интеграции с JSP]])). Однако, перед добавлением новых компонентов, реализация ''ViewHandler'''а должна предварительно проверить, не содержится ли уже указанный компонент в дереве компонентов. Если уже содержится (возможно из-за того, что предыдущие фазы создали его заранее), должны использоваться свойства и атрибуты уже существующего компонента((Вот этот момент я плохо понял.)).
 +  * Ни при каких условиях не должен выбираться компонент для рендеринга, если у какого-либо из его родительских компонентов свойство ''rendersChildren'' установлено в ''true''. В таких случаях тот родительский компонент должен сам рендерить своих детей, когда он будет выбран.
 +  * Если метод [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIComponent.html#isRendered()|isRendered()]] у компонента возвращает ''false'', то рендерер для этого компонента не должен производить никакой разметки и никакой из детей или facet'ов этого компонента не должен быть отрендерен.
 +
 +Для компонентов, которые реализуют интерфейс [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/ValueHolder.html|ValueHolder]] (например, ''UIInput'' и ''UIOutput''), должно выполняться преобразование данных в соответствии с тем, как это описано в [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/component/UIOutput.html|документации]] к ''UIOutput''.
 +
 +По завершении рендеринга, конечное состояние вида должно быть сохранено посредством методов класса [[http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/javax/faces/application/StateManager.html|StateManager]]. Эта информация о состоянии должна быть доступна при последующем запросе, чтобы ей можно было воспользоваться в фазе [[#restore.view|Restore View]]. Более подробная информацию о ''StateManager'''е можно найти [[StateManager|здесь]].
 +
 +===== На практике =====
 +Делаем простенький [[.:PhaseListener]]:
 +
 +<code java>
 +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;
 +  }
 +}
 +</code>
 +
 +И наблюдаем следующую картину:
 +  * Прямое обращение к странице:
 +
 +  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-приложению (например, вы хотите [[.:with_jaas|защитить]] какие-то страницы от просмотра, то необходимо либо писать фильтр, чтобы запросы к JSF-сервлету проходили сначала через него, либо писать [[.:PhaseListener]], который будет висеть на фазе ''RESTORE_VIEW''.
 +
 +
 +===== Ссылки по теме =====
 +  * [[http://www-128.ibm.com/developerworks/java/library/j-jsf2|JSF for nonbelievers: The JSF application lifecycle]] (англ.)