skillup

技術ブログ

Java

スコープアノテーションとCDIについて

投稿日:2015年7月11日 更新日:

JavaEEで個人的に鬼門がCDIとスコープだと思っています。

ここが少しずつ分かりかけてきたんでメモします。

まずJavaEEでは変数の生存期間をアノテーションで定義します。

これがなかなか分かりにくく、慣れるまで時間がかかりました。

ちょっと簡単な例で見ていきましょう。

スコープアノテーション

サンプル

counter

上記のようなボタンを押したらカウントアップするようなプログラムを作るとします。

ファイル構成は単純化するために画面側(counter.xhtml)、プログラム側(SampleBean.java)にともに1つとします。

画面側のファイルcounter.xhtmlについては常に下記のものとします。

プロパティの表示とカウントアップのメソッドのみにします。

外部ファイルは使わず変数のみでこれを実装したいと思います。

@RequestScoped

次にプログラムのほうを見ていきましょう。

リクエストスコープの場合、1回の通信の間のみ変数を保存します。

そのためボタンを押して表示するまでしか変数(正確にはSampleBean)は生きていません。

  1. ボタンを押す
  2. SampleBeanが生成される
  3. ロジックが処理される
  4. 値がレンダリングされる
  5. SampleBeanが破棄される

という流れになるのでカウンタは常に1のままです。(上のサイクルは完全に正確ではないと思いますが、おおまかなイメージです。)

@SessionScoped

次にセッションビーンです。SampleBeanを若干書き換えます。

この場合、ボタンを押すたびにカウンターが上がります。

この場合、流れは下記のようになります。

  1. すでにSampleBeanを生成されている
  2. ボタンを押す
  3. ロジックが処理される
  4. 値がレンダリングされる
  5. セッションが切れた時点でSampleBeanが破棄される

リクエストスコープとちがい、セッションスコープはglassfishがあらかじめ、オブジェクトを生成します。

なので、一定期間(セッションが有効な間は)、値が生存します。

スコープアノテーションで注意すること

importの種類

スコープアノテーションですがimportが複数あるようなので正確なものを選びましょう。そうしないと動きません。下記がそれぞれ正常なスコープアノテーションのimoportです。

javax.enterprise.context.RequestScoped

javax.enterprise.context.SessionScoped

Serializableインターフェイス

なお、リクエストではなくセッションのようにデータを保存する場合、シリアライズという処理を行います。これを忘れるとそもそもビルドに失敗するので注意しましょう。

参考リンク

直列化(serialize:シリアライズ)

ちなみにスコープはリクエストとセッションではなく、下記のように数種類あります。

viewScoped

リクエストにより表示されたJSFが他の画面に切り替わるまでは値が生存します。今回のカウンターの例でいうと値が残ります。

ApplicationScoped

ウェブアプリケーションが実行されている間は値が残ります。

ConversationScoped

1回以上のリクエストの間で開始と終了をプログラマが明確に定義できます。

参考リンク

1-1) スコープ(Scope)とBeanの生成と破棄

なお、当然のことながら不要に値の生存期間を増やすことは

  • メモリの圧迫
  • バグの温床

になりますので、できる限り狭くすることが大切です。

CDIという考え方

ManagedBeanについて説明した後で同時にCDIについても理解しておきたいと思います。

まずサンプルから見ていきましょう。

上記のようなカウンターですが、プログラムをSampleBean.javaのみでなく、

SampleBean.java

Counter.javaの2種類にし、

カウントアップに関してはCounter.javaで行うこととします。

ソース

SampleBean.java

Counter.java

このプログラムを動かすとクリックするごとにカウントアップします。

これはSampleBean.javaがリクエストスコープですが、Counter.javaがセッションスコープなので、値が保存されているためです。

CDI(Context and Dependency Injection)の定義

CDIの定義ですがいろんなところで見聞きしたのですがようわかりませんでした・・・

今では簡単に下記のような解釈をしております。

  • new演算子を使わずにオブジェクトを取得する仕組みであり、生成や破棄の手間がいらない(=正確にはコンテナ(この場合はglassfish)が管理するのでプログラマが管理しなくてよい。)
  • 他のオブジェクトを使用する場合は@Injectというアノテーションを付与するだけでそのオブジェクトが使用できる。

上記の例などがそうですが、Counterオブジェクトに関してnewを使用せずとも、@Injectを使用するだけで使えてしまいます。

インスタンスの生成、破棄などはプログラマ側がしてしなくてよいのです。

ちなみにこのようにして管理されたオブジェクトをCDIビーンと呼びます。

今になってようやくメリットが理解でき始めました(爆)

ちなみにManagedBeanもオブジェクトの生成や破棄をしていないのに、使用できたのはこのCDIの特性を持っているからです。

CDIの条件

このように便利なCDIですが、どんなクラスでもなれるというわけではありません。

CDIとはコンテナがインスタンスを生成することです。

なので、インスタンスが生成できなければCDIビーンにはなれません。これはいわゆる通常のクラスといっしょなので

  • 具象クラスであること
  • 引数なしのディフォルトコンストラクタをもつこと
  • static付きのインナークラスでないこと

という条件が必要になってきます。

CDIのスコープ

CDIのスコープもアノテーションを付与して判断します。アノテーションの種類はさきほど紹介したものといっしょですが、ViewScopedはJSFで定義されているものなので、通常のCDIビーンで定義はできないようです。

また呼び出し側のスコープ(この場合でいうとSampleBean.java)に依存するという@Dependentというスコープも存在します。

CDIって聞いたときはわけがわからんかったんですが、アプリを実際に作りつつようやくメリットが理解できてきました。

ちなみに今回は下記書籍のサンプルを参考にしました。

結構前に読んだ時はよくわからなかったんですが、今読んでみると大変すっきりしました。

参考書籍

わかりやすいJavaEEウェブシステム入門

-Java
-,

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

no image

JSFを使ったAjax

現在開発中のサブプロジェクトでJSFからAjaxを使う機会があったので紹介します。 仕様自体は非常にシンプルで下記の画面でプルダウンが変更になったときにDBを見に行って画面の値を更新するような仕様です …

no image

CDIの循環依存エラー&JSFでの条件分岐について

本日は小ネタなので2つのネタをまとめます。 Contents1 CDI循環依存エラー2 JSFでの条件分岐 CDI循環依存エラー 半年近くやってますが、いまだにJavaEEでエラーがでてくると焦ります …

no image

複合コンポーネント

JSFではラベルやテキストといったコンポーネントをグループ化し、一つのまとまりとすることができます。 メリットとしては似たような部分をテンプレート化しておくことで保守性を向上させることができます。 さ …

no image

リソースファイルの読み込み

リソースバンドルは基本的にJSFから参照して呼び出すことが多いんですが、もちろんプログラム内からも呼び出すことはできます。 書き方も単純でリソースバンドルをインスタンス生成してメッセージのキーで呼び出 …

no image

データベース文字コード対策他

Contents1 JDBC文字化け対策2 BigDecimal2.1 ソース2.2 参考リンク3 CreateQuery4 jsfのconvertNumber JDBC文字化け対策 データベースがu …