「JSFのライフサイクル」というものについて解説する前に、まず「UIコンポーネント・ツリー」というものについてお話ししましょう。
JSFでは、JSF独自のタグでJSPの画面を構成します。JSFのアプリケーションの内部では、このタグの階層構造をJavaオブジェクトの階層構造に置き換えたものを管理しています。このJavaオブジェクトの階層構造のことを「コンポーネント・ツリー」と言います。
JSFでは、この「コンポーネント・ツリー」を使って、さまざまな処理を行います。
この連載の第1回(2005年1月号)では、JSFのReference Implementation (RI)に付属しているguessNumberサンプルアプリケーションについて解説しました。では、guessNumberでは、どのようなUIコンポーネント・ツリーが構築されるのでしょうか?
guessNumberを起動すると、図10.1[guessNumberの最初のJSP]のようなJSPが表示されます。
このJSPのうち、JSFのタグの部分だけを抜き出すと、次のようになります。
<f:view> <h:form id="helloForm"> <h:outputText ...... /> <h:outputText ...... /> <h:graphicImage ...... /> <h:inputText ...... /> <h:commandButton ...... /> <h:message ...... /> </h:form> </f:view>
f:viewがいちばん上の階層にあり、その下にh:formがあり、さらにその下にh:outputText, h:graphicImage, h:inputText, h:commandButton, h:messageといったタグがあることがわかります。
では、UIコンポーネント・ツリーではどのようになるのでしょうか?図10.2[guessNumberのUIコンポーネント・ツリー]が、このJSPに対応するUIコンポーネント・ツリーです。
UIコンポーネント・ツリーの各コンポーネントとJSPのタグが対応しています。その対応関係を表10.1[UIコンポーネント・ツリーとJSPのタグの対応]で見てみましょう。
ツリー中のJavaオブジェクト | JSPのタグ |
UIViewRoot | f:view |
UIForm | h:form |
UIOutputText | h:outputText |
UIInputText | h:inputText |
UICommandButton | h:commandButton |
UIMessage | h:message |
この表とUIコンポーネント・ツリーの階層構造、そしてJSPのタグの階層構造を見比べると、UIコンポーネント・ツリーとJSPのタグの2つの階層構造が一致していることがわかりますね。
このUIコンポーネント・ツリーは、JSFのアプリケーションの中で保存されている必要があります。
この保存場所は2通りあります。1つめはサーバ上です。この場合は、HttpSessionに保存されます。2つめはクライアント上です。この場合は、表示されるHTMLのhiddenタグに記述されます。
デフォルトではサーバ上で保存されることになります。クライアント上で保存したいときには、web.xmlで次のように設定します。
<context-param> <param-name> javax.faces.STATE_SAVING_METHOD </param-name> <param-value>client</param-value> </context-param>
この章では、JSFのイベント処理について解説します。
まず、「イベント」とは、アプリケーションで行われる操作のことを指します。例えば「ボタンを押す」とか、「入力フィールドにデータを入力する」といった操作がイベントになります。
そして「イベント処理」とは、ある特定のイベントが起こったときに、何らかの処理を行うことです。
イベント処理は、大きく分けて2つの部分で処理が行われます。
まず、1つめは「イベント発生元」です。これは、ボタン・テキストフィールドなどイベントが起こる場所です。
2つめは「イベントリスナー」です。これは、発生したイベントを受け取り、イベントに対応する処理を行うところです。
JSFでは、2種類のイベント処理を行います。
1つめは、「Actionイベント」です。これは、ボタンやハイパーリンクが押されたときに発生するイベントです。
2つめは、「Value Changeイベント」です。これは、ボタンやハイパーリンクが押されたとき、対象となるUIコンポーネントが変化していたら発生するイベントです。
JSFのライフサイクルとは、「JSFのアプリケーションが、Webブラウザからリクエストを受け取ってからレスポンスを返すまでの処理手順」のことを言います。
ライフサイクルには6つのフェーズがあります。それぞれの概要は次の通りです。
それでは、guessNumberサンプルアプリケーションの動作を確認しながら、ライフサイクルの各フェーズの動きを追ってみましょう。
guessNumberサンプルアプリケーションは、第1回(2005年1月号)でもご紹介したように、Dukeくんが考えている数を当ててみるアプリケーションです(図10.3[guessNumberの動作]参照)。
ここでは、Webブラウザで0〜10までの数を入力して"Submit"ボタンを押すとWebブラウザからJSFのアプリケーションに対してリクエストが行われます。その後でレスポンスとして「あたり」か「はずれ」の画面が出力されます。このリクエストからレスポンスにいたるまでのライフサイクルについて見ていきます。
WebブラウザからのリクエストがあってJSFのアプリケーションが呼ばれた場合、ライフサイクルはこのフェーズからはじまります。
このフェーズには、次の2つのパターンがあります。
Webブラウザからのリクエストが、このJSFアプリケーションに対する、最初のリクエストである場合には、次のような手順で処理が実行されます。
JSFアプリケーション内でのページ遷移によって、JSFページにアクセスがあった場合には、
という処理が行われます。
guessNumberでは、前掲の図10.1[guessNumberの最初のJSP]の画面に対応する、図10.2[guessNumberのUIコンポーネント・ツリー]のようなUIコンポーネント・ツリーが復元されます。
このフェーズでは、リクエストのパラメータ情報を、UIコンポーネント・ツリーに設定します。
guessNumberの場合、図10.1[guessNumberの最初のJSP]のように、h:inputTextタグに入力された"9"がUIInputにセットされるのです。
h:commandButtonやh:commandLinkが押された場合、アクションイベントが発生して、アクションイベントが「イベント・キュー」に蓄えられます。
このとき、これらのイベントをどのフェーズで処理するか、アプリケーション制作者が調整できます。
JSPでのh:commandButtonタグで、以下のようにimmediate属性の値がtrueになっているとき、アクションリスナへの通知とアクションの実行は、このフェーズで行われます。これ以降のフェーズ(Managed Beanへの値の設定など)を実行せずとも処理できるので、キャンセルボタンなどの実現に使われます。
<h:commandButton immediate="true" action="cancel" value="キャンセル" />
immediate属性の値がfalseのとき、アクションリスナへの通知とアクションの実行は、「(5)アプリケーションの起動」フェーズで行われます。immediate属性を指定しなかったときのデフォルト値はfalseになります。
<h:inputText immediate="true" value="#{bean.quantity}" />
h:inputTextなどで値が変更された場合、immediate属性の値がtrueのとき、このフェーズで当該コンポーネントに関する次の処理が行われます。
immediate属性の値がfalseのとき、Value Changeリスナへの通知とリスナの実行は、「(5)アプリケーションの起動」フェーズで行われます。immediate属性を指定しなかったときのデフォルト値はfalseになります。
バリデータが設定されているときに実行されます。
(必要があれば)まずコンバータを実行します。その後でバリデータを実行します。
バリデータが成功なら、Value Changeイベントがイベントキューに蓄えられます。失敗なら、「(6)レスポンスのレンダリング」フェーズに移ります。
guessNumberでは、ValidatorであるUserNumberBean.validateメソッドを実行しています。
このフェーズでは、UIコンポーネント・ツリーの値をManaged Beanのプロパティにセットします。
必要があれば、まずコンバータを実行します。コンバータに失敗したら、「(6)レスポンスのレンダリング」フェーズに移ります。
その後で、Value Binding式で指定されたManaged Beanのプロパティに、UIコンポーネントの値をセットします。
guessNumberの場合には、h:inputTextのValue Binding式で指定されているuserNumberプロパティはInteger型になります。そこで、まずコンバータを実行して、入力データをIntegerに変換します。その後でuserNumberプロパティに格納するのです。
このフェーズでは、まずこれまでイベントキューに蓄積されたアクションイベントとValue Changeイベントを、それぞれのイベントに通知します。
その後でAction Methodを実行します。
そして、次の遷移先が決まります。
guessNumberでは、action属性の値とfaces-config.xmlでの設定によって、次の画面遷移先が決まります。
<h:commandButton id="submit" action="success" value="Submit" />
このフェーズでは、遷移先画面のUIコンポーネントツリーから、ブラウザに返すレスポンスを生成する処理を行います。
そのためには、まず遷移先画面がどこかわかっている必要があります。しかし、これまでのフェーズで、次はどの画面に移り変わるかわかっているはずです。
通常は「(5)アプリケーションの起動」で遷移先が決まります。しかし、次のようなケースでは、エラーが起きているため、元の画面にとどまる場合もあります。
遷移先が決定すると、次の順番で処理が行われます。
guessNumberでは、次のようになります。