Powered by SmartDoc

JSFのライフサイクル

UIコンポーネント・ツリー

UIコンポーネント・ツリーとは

「JSFのライフサイクル」というものについて解説する前に、まず「UIコンポーネント・ツリー」というものについてお話ししましょう。

JSFでは、JSF独自のタグでJSPの画面を構成します。JSFのアプリケーションの内部では、このタグの階層構造をJavaオブジェクトの階層構造に置き換えたものを管理しています。このJavaオブジェクトの階層構造のことを「コンポーネント・ツリー」と言います。

JSFでは、この「コンポーネント・ツリー」を使って、さまざまな処理を行います。

guessNumber と UIコンポーネント・ツリー

この連載の第1回(2005年1月号)では、JSFのReference Implementation (RI)に付属しているguessNumberサンプルアプリケーションについて解説しました。では、guessNumberでは、どのようなUIコンポーネント・ツリーが構築されるのでしょうか?

guessNumberを起動すると、図10.1[guessNumberの最初のJSP]のようなJSPが表示されます。

guessNumberの最初の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コンポーネント・ツリーです。

guessNumberのUIコンポーネント・ツリー

UIコンポーネント・ツリーの各コンポーネントとJSPのタグが対応しています。その対応関係を表10.1[UIコンポーネント・ツリーとJSPのタグの対応]で見てみましょう。

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コンポーネント・ツリーの保存場所

この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でのイベント処理

イベント処理とは何か

この章では、JSFのイベント処理について解説します。

まず、「イベント」とは、アプリケーションで行われる操作のことを指します。例えば「ボタンを押す」とか、「入力フィールドにデータを入力する」といった操作がイベントになります。

そして「イベント処理」とは、ある特定のイベントが起こったときに、何らかの処理を行うことです。

イベント処理のしくみ

イベント処理は、大きく分けて2つの部分で処理が行われます。

まず、1つめは「イベント発生元」です。これは、ボタン・テキストフィールドなどイベントが起こる場所です。

2つめは「イベントリスナー」です。これは、発生したイベントを受け取り、イベントに対応する処理を行うところです。

JSFでの2種類のイベント処理

JSFでは、2種類のイベント処理を行います。

1つめは、「Actionイベント」です。これは、ボタンやハイパーリンクが押されたときに発生するイベントです。

2つめは、「Value Changeイベント」です。これは、ボタンやハイパーリンクが押されたとき、対象となるUIコンポーネントが変化していたら発生するイベントです。

JSFのライフサイクル

JSFのライフサイクルとは、「JSFのアプリケーションが、Webブラウザからリクエストを受け取ってからレスポンスを返すまでの処理手順」のことを言います。

ライフサイクルには6つのフェーズがあります。それぞれの概要は次の通りです。

(1)ビューの復元
リクエスト元の画面のUIコンポーネントツリーを復元
(2)リクエスト値の適用
リクエストのときのパラメータ情報をUIコンポーネントツリーにセットする
(3)入力値の検証
設定された値にコンバータとバリデータを適用
(4)モデル値の更新
UIコンポーネントの値をManaged Beanのプロパティにセット
(5)アプリケーションの起動
Action Methodを実行して遷移先を決定
(6)レスポンスのレンダリング
遷移先画面のUIコンポーネントツリーから、ブラウザに返すレスポンスを生成

ライフサイクルの詳細

それでは、guessNumberサンプルアプリケーションの動作を確認しながら、ライフサイクルの各フェーズの動きを追ってみましょう。

guessNumberサンプルアプリケーションは、第1回(2005年1月号)でもご紹介したように、Dukeくんが考えている数を当ててみるアプリケーションです(図10.3[guessNumberの動作]参照)。

guessNumberの動作

ここでは、Webブラウザで0〜10までの数を入力して"Submit"ボタンを押すとWebブラウザからJSFのアプリケーションに対してリクエストが行われます。その後でレスポンスとして「あたり」か「はずれ」の画面が出力されます。このリクエストからレスポンスにいたるまでのライフサイクルについて見ていきます。

(1) ビューの復元

WebブラウザからのリクエストがあってJSFのアプリケーションが呼ばれた場合、ライフサイクルはこのフェーズからはじまります。

このフェーズには、次の2つのパターンがあります。

Webブラウザからのリクエストが、このJSFアプリケーションに対する、最初のリクエストである場合には、次のような手順で処理が実行されます。

  1. 空のUIコンポーネント・ツリーを生成
  2. 「(6)レスポンスのレンダリング」フェーズに移る

JSFアプリケーション内でのページ遷移によって、JSFページにアクセスがあった場合には、

  1. サーバかクライアントから、保存されているUIコンポーネント・ツリーを復元

という処理が行われます。

guessNumberでは、前掲の図10.1[guessNumberの最初のJSP]の画面に対応する、図10.2[guessNumberのUIコンポーネント・ツリー]のようなUIコンポーネント・ツリーが復元されます。

(2) リクエスト値の適用

このフェーズでは、リクエストのパラメータ情報を、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になります。

Value Change イベント

<h:inputText immediate="true"  value="#{bean.quantity}" />

h:inputTextなどで値が変更された場合、immediate属性の値がtrueのとき、このフェーズで当該コンポーネントに関する次の処理が行われます。

  1. コンバータ
  2. バリデータ
  3. Managed Beanのプロパティへの値のセット
  4. Value Changeリスナの実行

immediate属性の値がfalseのとき、Value Changeリスナへの通知とリスナの実行は、「(5)アプリケーションの起動」フェーズで行われます。immediate属性を指定しなかったときのデフォルト値はfalseになります。

(3) 入力値の検証

バリデータが設定されているときに実行されます。

(必要があれば)まずコンバータを実行します。その後でバリデータを実行します。

バリデータが成功なら、Value Changeイベントがイベントキューに蓄えられます。失敗なら、「(6)レスポンスのレンダリング」フェーズに移ります。

guessNumberでは、ValidatorであるUserNumberBean.validateメソッドを実行しています。

(4) モデル値の更新

このフェーズでは、UIコンポーネント・ツリーの値をManaged Beanのプロパティにセットします。

必要があれば、まずコンバータを実行します。コンバータに失敗したら、「(6)レスポンスのレンダリング」フェーズに移ります。

その後で、Value Binding式で指定されたManaged Beanのプロパティに、UIコンポーネントの値をセットします。

guessNumberの場合には、h:inputTextのValue Binding式で指定されているuserNumberプロパティはInteger型になります。そこで、まずコンバータを実行して、入力データをIntegerに変換します。その後でuserNumberプロパティに格納するのです。

(5) アプリケーションの起動

このフェーズでは、まずこれまでイベントキューに蓄積されたアクションイベントとValue Changeイベントを、それぞれのイベントに通知します。

その後でAction Methodを実行します。

そして、次の遷移先が決まります。

guessNumberでは、action属性の値とfaces-config.xmlでの設定によって、次の画面遷移先が決まります。

<h:commandButton id="submit" action="success" value="Submit" />

(6) レスポンスのレンダリング

このフェーズでは、遷移先画面のUIコンポーネントツリーから、ブラウザに返すレスポンスを生成する処理を行います。

そのためには、まず遷移先画面がどこかわかっている必要があります。しかし、これまでのフェーズで、次はどの画面に移り変わるかわかっているはずです。

通常は「(5)アプリケーションの起動」で遷移先が決まります。しかし、次のようなケースでは、エラーが起きているため、元の画面にとどまる場合もあります。

遷移先が決定すると、次の順番で処理が行われます。

  1. 遷移先に移り変わる。
  2. UIコンポーネント・ツリーを生成する。
  3. Managed Beanの値をUIコンポーネント・ツリーに設定する。
  4. レスポンスで返すHTMLを生成する。
  5. サーバかクライアントにUIコンポーネント・ツリーを保存する。

guessNumberでは、次のようになります。

  1. h:inputTextのaction属性の値とfaces-config.xmlの設定によって、response.jspに遷移する。
  2. response.jspでは、Managed Beanのresponseプロパティの値をUIコンポーネントに設定する。このとき、「あたり」か「はずれ」かをチェックする。
  3. UIコンポーネント・ツリーからHTMLを生成する。