JSF ライフサイクルとか

作成 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を使って簡単なログ出力を行ってみます。

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.javaHogeConverter.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のソースはこちらから入手できます。

以下の部分が主なクラス、メソッドとなります。

自分の環境で、ソースをアタッチしてステップ実行すると、 けっこう理解しやすいのではないかと思います。

@TODO

参考

@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


Back Top