Java EE Managed Beanについて (Java EE Advent Calendar2013 19日目)

このエントリは Java EE Advent Calendar 2013 の 19 日目のエントリです。前日は @さんによる、「JSF 2.2 でさらに便利になったMarkupを使ってみよう」でした。

はじめに

Java EEの仕様の中でも立ち位置が微妙すぎて、かの金魚本でもガン無視されちゃっている Java EE Managed Bean のお話を取り上げます。
(JSF の Managed Bean のお話じゃないのでご注意を!)
Managed Bean は JSR-316 Java EE 6 の一部として含まれている仕様で、仕様書である "Managed Beans 1.0 Specification" は本文がたったの 11 ページというとてもあっさりとした仕様です。
重厚長大なイメージのある Java EE 仕様の中で、このあっさりさは際立っているように思えます。

Managed Bean とは?

Managed Bean は Java EE アプリケーションサーバーのコンテナによって管理される JavaBean です。もう少し具体的に言うと EJB のスーパーセットです。
コンテナの管理下に入ることにより、次のような機能がサポートされます。

  • リソースのインジェクション
  • ライフサイクルに応じたコールバックメソッド
  • インターセプタの適用

EJB と異なり、次のような機能はサポートされません。

作り方

Managed Bean の作り方は簡単です。POJO に @javax.annotation.ManagedBean アノテーションを付与するだけで OK です。
POJO でいいのですが、次のような制約があります。

  • final ではない
  • 抽象クラスではない
  • 内部クラスの場合、static なクラスであること (ネストクラスであること)

Serializable であることは強制されません。
JSFアノテーション (@javax.faces.bean.ManagedBean) を間違えて付けてしまわないように注意しましょう。

@ManagedBean("myManagedBean")
public class MyManagedBean {
    @Resources
    private OtherResource otherResource;
    /**
     * コンポーネント初期化後に呼び出されるメソッド
     */
    @PostConstruce
    public void setUp() {
    }
    /**
     * コンポーネント破棄前に呼び出されるメソッド
     */
    @PreDestroy
    public void cleanup() {
    }
}

@ManagedBean アノテーションの属性には JNDI ルックアップを行う際に使う名前を指定します。Managed Bean を利用するのに後述の @Resource アノテーションしか使わないのならば省略しても OK ですが、JNDI ルックアップを行う際には必ず記述する必要があります。

EJB と同様、@PostConstruct、@PreDestroy アノテーションを使うことができ、インスタンス生成直後、インスタンス破棄前に処理を挟み込むことが可能になります。
また、@Resource を使って他のコンテナ管理リソースをインジェクトすることもできます。
基本的な作り方は EJB と全く変わらないことが分かると思います。

使い方

Managed Bean の使い方は次の2通りです。

  1. @Resource アノテーションによるインジェクション
  2. JNDI ルックアップ

まず、@Resource アノテーションを使ったインジェクションはとても簡単です。インジェクトしたいフィールドなり setter メソッドなりに指定すれば OK です。

@Stateless
public class MyEJB {
    @Resource
    private MyManagedBean myManagedBean;
    ...
}

JNDI ルックアップを行う場合、ルックアップに使用する名前空間は次のようになります。

アプリケーションレベルの名前空間
java:app//
モジュール内での名前空間
java:module/

はデプロイしたアーカイブの名前 (WAR ファイルの名前など) になります。
は @ManagedBean アノテーションに指定した属性になります。EJBCDI と違って、デフォルトでは名前が生成されないので注意してください。
モジュールをまたいで利用することは少ないでしょうから、通常は後者の名前空間を使って取得することになるでしょう。

    InitialContext ic = new InitialContext();
    MyManagedbean myManagedBean = (MyManagedbean) ic.lookup("java:module/myManagedBean");

使いどころ

コンテナ管理オブジェクトにして、コンテナ提供の機能 (特にリソースインジェクション) を使いたいけど、EJB を使うまでもない、という場面で使うことになるでしょうか。
要はトランザクション境界にする必要のないコンポーネントの場合に使うといったところですかね。
以前自分が開発した JAX-RS アプリケーションでは、次のコンポーネントを Managed Bean にしました。

  • Repository クラス (DAO クラス)
  • JAX-RS のリソースクラス

そして、トランザクション境界となる Service コンポーネントを Stateless Session Bean としています。

...と書きましたが、それだったら CDI 使ったら良いんじゃないのー、という突っ込みが入りそうです。いや全くその通りだと思います。

Java EE 6 の仕様策定時、CDI はぎりぎりまで EE 6 に入るかどうか微妙で、Managed Bean はそんな中産まれた、微妙な立ち位置の仕様に見えます。

今年の JavaOne で、Java EE の将来を考えるというパネルディスカッションを聴いたのですが、そこでもこれからは CDI に寄せた方がいいんじゃないの?という雰囲気でした。
なので、今後は CDI を使うようにした方がいいように思えます。個人的には Managed Bean のシンプルな仕様は結構気に入っているんですけどね。

分からないこと

Managed Bean のインスタンス管理方式については仕様では明確になっていません。
アプリケーションサーバーの実装に任されており、恐らく Singleton Session Bean と同じ (1つしかインスタンスを作らない) 方式になっているものと思われますが、実際どうなんでしょう?
SLSB のようにインスタンスプールを作っている実装もあったりするのでしょうか?

というわけで、Managed Bean という今後日の目を見ることはないであろう、かわいそうな仕様についての紹介でしたw

明日はhachiroさんの予定です。