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
Next revision
Previous revision
Last revisionBoth sides next revision
jsf-ru:faq:request_lifecycle [2006/01/14 23:54] slonopotamusjsf-ru:faq:request_lifecycle [2006/07/08 20:13] slonopotamus
Line 1: Line 1:
-[[jsf-ru:faq|Назад к FAQ]] 
-====== JSF Request Lifecycle ====== 
  
- 
-===== В теории ===== 
- 
-Как нам подсказывает [[google>jsf request lifecycle|Google]] или та же спецификация по JSF, жизненный цикл JSF-запроса выглядит так: 
- 
-{{.:jsf-request-lifecycle.gif}} 
- 
-И всё бы хорошо, но. Эта модель **не работает**. А конкретно, она не работает тогда, когда к JSF-странице обращаются напрямую по ссылке, а не когда на неё переходят с другой JSF-страницы. В связи с этим, например, приходится жутко извращаться при [[with_jaas|интеграции JSF с JAAS]]. А когда обращаются напрямую, то **все** стадии, помеченные красным (application-level phases), на которых приложению проще всего взаимодействовать с JSF, **пропускаются**. Вас это радует? Меня это откровенно бесит. 
- 
-===== Описание стадий ===== 
- 
- 
- 
-==== 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''. 
jsf-ru/faq/request_lifecycle.txt · Last modified: 2019/06/12 16:08 by 127.0.0.1