作成 2004/4/29
ライフサイクルとかのメモ
JSFは以下の図のような流れでリクエストを処理します。
図 リクエスト処理のライフサイクル
(Spce の "2.1.2 Faces Request Generates Faces Response"の図を元にちょっと修正)
※Response CompleteはFaces Responseを返さないパターンだと思いますが、未調査
ValidationやConvertionのエラーがなければ、実線の流れで、6つのフェイズが順に実行されます。 点線はその他のフローです。例えばバリデーションでエラーがある場合は、Process Validationsフェイズの後、Render Responseに行きます。入力チェックでひっかかったとは、バッキングビーン自体の値は変更されない(Update Model Valuesは通らない)、アクションが実行されない(Invoke Applicationsは通らない)という流れが分かるような気もします。
以下の表はフェイズの一覧です。指定したactionやvalidatorは以下の場所で呼ばれます。
表 フェイズ
フェイズ | 説明/主な呼び出しメソッド | それはここで呼ばれる |
---|---|---|
Restore View | リクエストからUIコンポーネントのツリーを(再)構築します / いろいろ | |
Apply Request Values | リクエストの値が各UIコンポーネンに適用されます / UIComponent#processDecode(...) | UIComponent#decode() |
Process Validations | バリデーションが実行されます / UIComponent#processValidate(... | validator
valueChangeLister |
Update Model Values | モデルの値が更新されます / UIComponent#processUpdate(...) | |
Invoke Application | ビジネスロジックが実行されます / UIViewRoot#prcessApplication(...) | actionListener
action |
Render Response | レスポンスが生成されます / ViewHelper#renderView(...) | UIComponent#encodeBegin()/encodeEnd() |
なぜ、JSFでは、このように多くのフェイズを経由するようになったのか、については理解にいたってません。 ただ、フォームの値とモデルの値の間のコピー、入力エラー時のフロー処理など、 これまであーメンドクサイと思ってたことをJSFにおまかせすることは出来ると思われます。
というような話を聞いても、イマイチ ピンと来ないかもしれません。プログラムを実行して本当にこのフェイズが実行されているかを確認してみましょう。後で紹介するようにソースデバッグしてもいいですが、ここでは、まずPhaseListenerを使って簡単なログ出力を行ってみます。
PhaseListenerを使うと、フェイズの前後に割り込んで処理を行うことができます。
リスト MyPhaseListener.java
package hoge; 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) { System.out.println("[AFTER]" + event.getPhaseId()); } //後処理 public void beforePhase(PhaseEvent event) { System.out.println("[BEFORE]" + event.getPhaseId()); } //対象のフェイズIDを返す。ここでは全てのフェイズを指定。 public PhaseId getPhaseId() { return PhaseId.ANY_PHASE; } }
PhaseListenerをfaces-config.xmlに登録します。
<lifecycle> <phase-listener>hoge.MyPhaseListener</phase-listener> </lifecycle>
こんな感じのJSPページでボタンを押すと(別に何でもいいけど)、、、
<h:form> <h:inputText value="#{hoge.s}"/> <h:commandButton value="GOGO"/> </h:form>
次のような出力が表示されます。ちゃんとフェイズどおりですね。
[BEFORE]RESTORE_VIEW 1 [AFTER]RESTORE_VIEW 1 [BEFORE]RENDER_RESPONSE 6 [BEFORE]RESTORE_VIEW 1 [AFTER]RESTORE_VIEW 1 [BEFORE]RENDER_RESPONSE 6 [AFTER]RENDER_RESPONSE 6 [BEFORE]RESTORE_VIEW 1 [AFTER]RESTORE_VIEW 1 [BEFORE]APPLY_REQUEST_VALUES 2 [AFTER]APPLY_REQUEST_VALUES 2 [BEFORE]PROCESS_VALIDATIONS 3 [AFTER]PROCESS_VALIDATIONS 3 [BEFORE]UPDATE_MODEL_VALUES 4 [AFTER]UPDATE_MODEL_VALUES 4 [BEFORE]INVOKE_APPLICATION 5 [AFTER]INVOKE_APPLICATION 5 [BEFORE]RENDER_RESPONSE 6 [AFTER]RENDER_RESPONSE 6
最初にJSPを読んだときは以下のようになります。
[BEFORE]RESTORE_VIEW 1 [AFTER]RESTORE_VIEW 1 [BEFORE]RENDER_RESPONSE 6 [AFTER]RENDER_RESPONSE 6
またバリデーションでエラーになると、こんな感じになります(上のJSPコードではバリデーションをセットしてませんが)。
[BEFORE]RESTORE_VIEW 1 [AFTER]RESTORE_VIEW 1 [BEFORE]APPLY_REQUEST_VALUES 2 [AFTER]APPLY_REQUEST_VALUES 2 [BEFORE]PROCESS_VALIDATIONS 3 [AFTER]PROCESS_VALIDATIONS 3 [BEFORE]RENDER_RESPONSE 6 [AFTER]RENDER_RESPONSE 6
※ちなみにロギングのレベルをdebugにすると、元のJSFのログがわらわら出て、 それでもフェイズがなんとなく分かる。log4jの設定ファイルの例→log4j.xml。log4jを使う場合は、log4jの実装もlibに必要です。
リスナーやら何やらいろいろつけてためしてみました。 バッキングビーンのメソッドや、コンバータでは、適当なメッセージを標準出力しています。 (ロクなものではないですが、コードはこちら→ HogeBean.java、HogeConverter.java)
<h:form> <h:inputText value="#{hoge.s}" valueChangeListener="#{hoge.valueChanged}" validator="#{hoge.validate}" converter="hogeConverter" /> <h:commandButton actionListener="#{hoge.actionFired}" action="#{hoge.action}" value="GOGO"/> <h:messages/> </h:form>
実行すると以下のように出力されます。
[BEFORE]RESTORE_VIEW 1 [AFTER]RESTORE_VIEW 1 [BEFORE]APPLY_REQUEST_VALUES 2 [AFTER]APPLY_REQUEST_VALUES 2 [BEFORE]PROCESS_VALIDATIONS 3 <---converter:getAsObject <---validate <---valueChangeListener [AFTER]PROCESS_VALIDATIONS 3 [BEFORE]UPDATE_MODEL_VALUES 4 [AFTER]UPDATE_MODEL_VALUES 4 [BEFORE]INVOKE_APPLICATION 5 <---actionListener <---action [AFTER]INVOKE_APPLICATION 5 [BEFORE]RENDER_RESPONSE 6 <---converter:getAsString [AFTER]RENDER_RESPONSE 6
ソースを追って見ると、ライフサイクルがまた理解しやすいです。 細かい部分まで追わなければ、大筋は上のライフサイクルの図のとおりでけっこう簡単です。 ここではSunのRIを使います。 RIのソースはこちらから入手できます。
以下の部分が主なクラス、メソッドとなります。
自分の環境で、ソースをアタッチしてステップ実行すると、 けっこう理解しやすいのではないかと思います。
@IT JavaServer Facesを理解する
http://www.atmarkit.co.jp/fjava/special/jsf01/jsf01.html
Core JSF - Event Handling
http://www.horstmann.com/corejsf/
Spec - Chapter2 Request Processing Lifecycle