#Advent Calendarの10日目です
最近JavaEE7の本「わかりやすいJavaEE」を書きました。ウェブシステムの作り方を解説した本です。
Java言語初心者でも読めるJavaEEの本というコンセプトなんですが、そもそも「JavaEEと言われても・・・」と思う人、多いでしょうね。
そこで、今日は、JavaEEのあれこれをいろいろお話します。
JavaEE未経験者のためのガイダンスです。
この記事を通じて、「わたしもやってみよう」と思ってもらえれば幸いです。
#はじめのはじまり
Javaを使ってずいぶんになります。まだ、Javaコンパイラに日本語が通らない頃、"write once , run anywhere"ということばにひかれてやってみたのが最初でした。Javaでソケット通信の小さなプログラムを作ってみました。これが違うOSのマシンであっけなく動いたので、「こりゃすごい」と感動したのでした。
Appletでもいろいろ試してみましたが、当時は起動に10分くらいかかったので、実用にはなりませんでした。が、しばらくしてservletが登場したので状況が違ってきました。servletは、新しい時代の匂いがしていましたね。
ただその後のJ2EEは、とても触る気になれなかったので、(暇だったこともあって)「自分用のフレームワークを作ってみよう」と思い、1年ほどかけてJEF(Java Easy Framework)というのを作りました。JEFは5年ほど前まで開発を続けていて、eラーニングシステムなどの多くのシステム開発に使いました。
と、と、ところが、そのうちに世の中は大きく変わっていたのです。JavaEE5、そして、JavaEE6、JavaEE7。JEFのおかげで、Strutsなどは眼中になかったのですが、グプタさんのビデオを見てとても驚きました。ここまで便利になってしまうとは!
それで「わかりやすいJavaEE」というjavaEE7の本を書くことになるのですが、そのあたりのてんまつは「わかりやすいJavaEEの裏話」に書きましたのでそちらをご覧ください。
#JSFはやさしい!
やはりJSFですね。これがJavaEE7のキモです。とにかくウェブとのデータのやり取りや画面遷移が簡単です。
図で説明しましょう。次の図をご覧ください。
『わかりやすいJavaEE/3章ウェブアプリケーションの仕組み』より
左端の「バッキングビーン」というのがJavaプログラム、右側の「JSFページ」はJSFの画面(上段)、「Facelets」はそのソースコード(下段)です。
###ウェブとJavaプログラムとのデータ受け渡しがカンタン
まず、下段のFaceletsですが、h:などが付いていますが気にしないでください。おおむねHTMLと同じようなタグです。このようなタグはFaceletsタグといい、ウェブ画面を作るのに使います。もちろん、HTMLとの混在も可能です。FaceletsはHTMLを知っていればすぐに理解できます。
図のFaceletsは番号や氏名の入力欄のために、テキスト入力領域を定義しています。
h:inputText ・・・というあたりです。
この中で、value="#{meiboBean.number}" の部分がポイントです。
この{#・・・}の部分をEL式といい、Javaプログラムの変数との対応付けを行っています。
例えば、#{meiboBean.number}は、番号の入力値を左端のMeiboBeanというJavaプログラムの、numberというフィールド変数と対応づけるという意味です。また、#{meiboBean.name}は、名前の入力値を同じくnameというフィールド変数に対応づけています。
そうです、これだけで、ウェブで入力したデータがプログラムに渡されるのです。プログラム側では、原則、何もする必要はありません。逆に、プログラムで変数に値が入っていれば、それがウェブに表示されます。ウェブ上の項目とJavaプログラムの変数とを結びつけるこの仕組みを、バインドといいます。
バインドは単純ですが、とても強力な機能です。これがあるからJSFを使いたいと思わせるインパクトがあります。
###ページナビゲーションがカンタン
次に注目するのは、Faceletsの送信ボタンです。h:commandButtonと書いてあるやつです。
これが送信ボタンを表示するFaceletsタグです。
ボタン定義の中に、action="#{meiboBean.next()}" というEL式があります。これは、送信ボタンを押したときに実行する、Javaプログラムのメソッドを指定しています。
実際に、左端のJavaプログラムにはpublic String next() があります。送信ボタンが押されると、データを受け取った上で、さらにこのnextメソッドを実行するわけです。nextメソッドでは、実際にはフィールド変数をデータベースに保管するなどの操作をします。そして最後にreturn文で、次に表示するウェブ画面のファイル名を返します。これがページナビゲーションです。これだけで、次の画面(output.xhtml)が開きます。
面倒な設定ファイルはありません。だたこれだけです。このreturn文で指定するだけというシンプルさは本当に脱帽ものですね。
ちなみに、動作を確実にするには、
return "output.xhtml?faces-redirect=true"
のように、末尾に?faces-redirect=true
を付けます。
これを付けないと、画面遷移が不安定に(バグも含めて)なることがあるので、注意です。
ちなみに、同じ画面を再表示するには、return null; または、return ""; です。
###ウェブとJavaプログラムとの連携もカンタンです
このように、ウェブ画面(JSFページ)とバインドするJavaプログラムは、クラス定義の先頭に@Namedというアノテーションを付けるだけで設定できます。設定ファイルなんてありません。いろいろ書かなくても、決まりきったアノテーションを付けるだけでよいようにしよう、というのがJavaEEの方向性ですから。ちなみに、@RequestScopedは、バッキングビーンの生存期間(寿命と理解しましょう)を決めるアノテーションです。ほかにもまだいくつかありますが、これは、「1回のリクエストとレスポンスの間だけ存在する」という、最も短い寿命を指定する場合に使います。
なお、このようにJavaオブジェクトに寿命を与えるアイデアは、後でお話するCDIに関連しています。
###システムの使用感を実際に動いているサイトで体験してください
「わかりやすいJavaEE」では、18章でショッピングサイトを作成します。これを、動かして公開していますので、触ってみたい方は次のURLにアクセスしてください。
<雑貨屋さん.com>http://k-webs.jp:8080/zakka-ya-san/faces/index.xhtml
使用したライブラリを除くと、Javaのソースコードはわずか350行程度ですが、買い物をしてカートに入れ、注文までできます。
やはり、JSFの力が大きいです。特に動的に表を描く機能(h:dataTableタグを使います)はすぐれモノです。本でも詳しく解説しています。
・注文すると確認メールが届きます。(実際に購入はできません)
・ログインしなくても買い物はできますが、ログインすると購入履歴が見れます。
(ログインIDは本に書いてありますので見てください)
・データベースはページ単位のアクセス制御をしています。
・画像はデータベースから直接取り出して表示しています。
#データベース(JPA)もやさしい
Javaでデータベースと言えば、JDBCを使ってDAOパターンで、というのが相場でした。例えば、お話したJEFは、DAOパターンのクラス群のソースコードをSQLから自動生成する機能があり、それなりに便利でした。しかし、JPAを知ってしまうと、それはほとんど「おもちゃ」のようなものだったと分かったのでした。
###テーブル作成がカンタン
JPAの核心はORM(オブジェクト関係マッピング)です。これも名前は堅苦しいですが、中身は単純です。
さて、ORMは簡単にいうと、オブジェクトを含むレコード(エンティティといいます)をそのまま、データベースに記録できるようにする方法です。これは、SQLをよく知っていれば、「そんなアホな話!」と驚くところです。というのも、関係データベースにはオブジェクト型はありません。RDBは、Javaでいう基本データ型の世界ですから。
例えば、次のような(かなり作為的ですが)テーブルがあるとき、従来だとすぐにテーブル生成のSQLを書きますね。
1つは顧客表(CUSTOMERテーブル)、もう一つは顧客の追加情報表(INFORMATIONテーブル)です。そして、2つのテーブルを関連付けたいので、顧客表には追加情報キーのフィールドがあります。
キーで関連付けるのはRDBではごく普通のやり方です。
顧客番号 | 氏名 | 追加情報キー |
---|---|---|
cus001 | 田中宏 | 1 |
cus002 | 鈴木太郎 | 2 |
cus003 | 斎藤圭子 | 3 |
追加情報キー | 住所 | 電話番号 | 携帯番号 |
---|---|---|---|
1 | 東京都 | 03-333-3333 | 090-333-3333 |
2 | 大阪府 | 06-666-6666 | 090-666-6666 |
3 | 福岡市 | 092-999-9999 | 090-999-9999 |
しかし、JPAを利用するJavaでは、そもそもこのようなテーブルは考えなくてもいいのです。これは、オブジェクト指向の世界ではありません。JPAでは、テーブルを直接操作するのではなく、レコードの内容を表す簡単なオブジェクト(エンティティといいます)を使うだけです。エンティティを作成するには、@Entity
アノテーションを付けた次のようなクラスを作ります。エンティティは、フィールド変数とセッター、ゲッターがあるだけの単純なクラスです。エンティティクラスのフィールド変数がそのまま、データベースのフィールド(カラム)に対応すると考えてください。
@Entity
public class Customer implements Serializable {
@Id
private String customerId;
private String name;
private Information info;
// コンストラクタ(省略)
// アクセサメソッド(省略)
}
これはCustomerクラス(顧客エンティティ)です。そして、これからCustomer型のオブジェクトを作ることができ、また、CUSTOMERという名前のデータベーステーブルを自動作成して、オブジェクトを保管できます。ただ、注意すべきは、CustomerクラスにInformation型の変数があることです。いいのでしょうか?Infoはオブジェクトですけどね。
ところが、ここがJPAのエライところで、このままでいいのです。エンティティを作っただけで何もしていませんが、エンティティのフィールドにオブジェクトを書くと、それがORM(オブジェクト関係マッピング)です。エンティティの中にあるInformationオブジェクトは、自動的にデータベースのテーブル(INFORMATIONテーブル)に展開されます(テーブルは自動的に作られ対応付けられます)。なんと、すばらしいですね(^_^;
繰り返して言っておきますが、物理的なデータベーステーブルを直接操作するイメージは捨ててください。JPAでは、テーブルのレコードではなく、エンティティオブジェクトを読み書きするのです。
なお、オブジェクトとして使うので、次のようなInformationエンティティも作成しておく必要があります。これからInformationオブジェクトを作ります。
@Entity
public class Information implements Serializable {
@Id
private Long id;
private String address;
private String phone;
private String cellular;
// コンストラクタ(省略)
// アクセサメソッド(省略)
}
###(レコードに当たる)エンティティの保存がカンタン
データベースにレコードを保存するのはどうするのかというと、たとえて言うと、JavaでListに要素を入れたり出したりするような、あんな感じです。JDBCベースの操作よりもうんと簡単です。
それはJPAがエンティティマネジャーという、読み書きなどを行うためのオブジェクトを用意しているからです。プログラマはエンティティマネージャーのメソッドを使って、新規登録(persist)、更新(merge)、削除(remove)、検索(find)などを行います。このようなCRUD処理の全貌は「わかりやすいJavaEE」の<15章データベースの基礎>に例題を示して解説しています。中ではAJAXも使っていて、おもしろいですよ。
なお、18章では、いろいろなエンティティについてCRUD操作を行うための汎用クラス(TryCathcDb.java)を紹介しています。これを継承すると、特定のエンティティ用のDB処理クラスをわずか6行で定義できます。例えばCustomerエンティティのデータベース処理を行うCustomerDbクラスを作成するには次のようにします。クラスにコンストラクタを定義するだけですからとても簡単で、これだけでCustomerエンティティのCRUD処理コマンドやデータベース中の件数などを求めるコマンドが使えるようになります。
@stateless
public class CustomerDb exetends TryCatchDb {
public CustomerDb(){
super(Customer.class); // エンティティのクラス情報を与えて生成する
}
}
ではコマンドを使って具体的にはどうするのかというと、オブジェクトを作ってCustomerDbクラスのaddメソッドで保存するだけです。保存の操作は、バッキングビーンで行います。バッキングビーンは、冒頭にもでてきましたが、画面とのデータの受け渡しやデータベースへの保存、読み出しなどを行うJavaプログラムです。
@Named
@RequestScoped
public class MeigoBean {
// 途中省略
@EJB
CustomerDb db; // データベース処理を行うオブジェクト(上記参照)
// 途中省略
public void create(){
//
Information info1 = new Information(1, "東京都", "03-333-3333", "090-333-3333");
Customer c1 = new Customer("cus001", "田中宏", info1);
db.add(c1);
//
Information info2 = new Information(2, "大阪府", "06-666-6666", "090-666-6666");
Customer c2 = new Customer("cus002", "鈴木太郎", info2);
db.add(c2);
}
}
上記のCustomerDb db;
の部分は、データベース処理を実行するためのオブジェクトです。宣言だけしているように見えますが、JavaEEではDI(依存性注入)という仕組みによって、@EJBというアノテーションを付けるだけで、CustomerDb型のオブジェクトを変数dbに代入してくれます(<13章EJB>で解説しています)。オブジェクトをnewでつくらなくてもよいので最初は不思議な感じですが、すぐになれます。便利ですし。
それでdb.add()メソッドでオブジェクトをデータベースに登録します。これだけです。簡単ですね。特に、「データベースを操作している」という感じがしないほどです。「普通にオブジェクトを作って、データベースにaddした」と、そういう感覚でよいわけです(List操作みたいです)。
おっと、ひとつ忘れるところでした。
上記では、Customerエンティティに当たるc1を保存すると、同時にInfomationエンティティにあたるinfo1も保存されます。これをカスケードといいます。そして、カスケードするには、先ほどのエンティティで、実際に保存する方に、マッピングアノテーションを追加する必要があります。ここでは、実際に保存操作を行ったのはCustomerの方ですから、Customerエンティティのinfoフィールドにアノテーションを付けてカスケードの指定をします。
@Entity
public class Customer implements Serializable {
@Id
private String customerId;
private String name;
@OneToOne(cascade = {CascadeType.ALL})
private Information info;
// コンストラクタ(省略)
// アクセサメソッド(省略)
}
@OneToOneというアノテーションがでてきました。ここではカスケード指定のために使っています。カスケード指定は、CRUDのすべての操作でカスケードするためにCascade.ALLを指定します。これ以外に、特定の動作だけに限定する指定もあります。
ともあれ、ここまでの説明で、SQLやJDBCの操作が全く出てこないことがわかったかと思います。エンティティオブジェクトを初めて保存する時、RDBのテーブルは自動的に作成されます。NetBenasを使っていれば、データソースやコネクションプールといったサーバー回りの要素もすべて自動的に作成されます。
結局、JPAではJavaオブジェクトをそのまま扱うだけです。とてもスッキリしていますね。もうJDBCのガチな世界には戻る気がしません。
###もっと複雑なケースでもカンタン
上記の例は、One-to-One(1対1)の関係という、最も単純なORMの例でした。これ以外に、例えば、職員と所属の関係のように、異なる職員が同じ所属に属す場合にはMany-to-Oneの関係になります。職員エンティティは、そのフィールドに所属オブジェクトを持ちますが、いろいろな職員が同じ所属オブジェクトを持っているケースです。データベース的には職員テーブルと所属テーブルを「結合テーブル」を使って連結するのですが、JPAではそれを意識する必要はありません。JPAが自動的に処理するので、プログラマは、Javaベースのプログラムだけで、データベース処理が可能です。
また、One-to-Many、May-to-Manyの関係や、お互いに参照し合う双方向の関係など、複雑な関係はまだありますが、どれも簡単な原則にしたがってアノテーションを使うだけで、適切なデータベースへのマッピングを自動的に行ってくれるのがJPAです。データベーステーブルで考えるのではなく、Javaオブジェクトのエンティティベースでシステムを構築できるのがJPAの大きな利点です(動かせる例題による解説が「わかりやすいJava」の「17章オブジェクト関係マッピング」にあります)。
また、SQLに似たJPQLというオブジェクトベースの問い合わせ言語を使うことができます。複雑な問い合わせはこれを使います(詳細は「18章JPQLとウェブアプリケーション」をご覧ください)。
<追記2016.10>
JPQLについて、本の内容を補足するさらに詳細な解説を、サポートブログ<JavaIndex>わかりやすいJPAに書きました。JPQL、エンティティグラフ、ロック機構まで12回に分けて解説しています。この他、JavaIndexにはJavaSE、JavaEEに関する有益な情報がありますので、ぜひ一度ご覧ください。
#CDIとかEJBって何?
JavaEEの難点はコトバが難しいところです。でもコトバに惑わされないようにしましょう。実は、とても便利な仕組みが、コトバの裏にかくされているからです。ここでは、コトバを大胆に超訳しつつ解説します。
##いくつもの王国がある
CDIは「コンテキストと依存性注入」と訳されていますが、これでは、正直な話、何のことがわかりませんね。EJB(Enterprise JavaBeans)に至っては日本語の訳すらないのでさっぱりです。そもそも、JavaEEはJSF、JPA、CDI、EJBなどの直観的には意味のわからない3文字英語をやたら振り回すので、困ってしまいます。確かに、それぞれが違う王国を築いていて、違うサービスを提供しているのですが、ことばだけからは皆目わかりません。
そこで、これらが何をやってくれるのか、そこに注目して考えてみましょう。
###EJBは意外に使えるヤツです
JSFやJPAはこれまで見たように役割がはっきりしています。ひとまずOKですね。
EJB(Enterprise JavaBeans)は、以前は魔王の館みたいな感じがしていたのですが、いまでは、データベース処理などのビジネスロジックをカンタンに書けるようにしてくれる強い味方です。面倒なことは何もありません。クラスの先頭に@Statelessなどのアノテーションを付けるだけでEJBのクラスになります。
例えば、データベースではエラー発生時の対応などが必要ですが、EJBはそれを自動的にやってくれるので、書き手は気を使う必要がないという利点があります。ですからJPAを操作するプログラムは、基本的にEJBのクラスを作って作成します。
そのほかにも、定期的に起動するタイマー処理を仕込むとか、JavaSEではとても苦労したマルチスレッドをアノテーション1つで可能にするとか、シングルトンみたいなデザインパターンが@Singletonというアノテーションだけでできるなど、EJBでなくてはできないようなことがいくつもあります。
###ではCDIは?
実は、先に解説したバッキングビーンがCDIのクラスです。
@RequestScopedなどをスコープアノテーションといいますが、スコープアノテーションを付けたクラスがCDIのクラスです。他に@SessionScoped(セッション継続の間の寿命)とか、@ApplicationScoped(プログラムが終了するまでの寿命)などがありますが、スコープアノテーションで寿命が決まっていることが大きな特徴です。CDIとは結局、寿命のあるオブジェクトを使えるサービスです。
で、どんな便利さがあるかというと、「バッキングビーンとして使えた」と、ほとんどそこに尽きます。
CDIのオブジェクトはスコープアノテーションによって寿命が決まっています。これはバッキングビーンを作る時にとても便利です。多くの同時アクセスが見込まれるウェブシステムでは、寿命が尽きたオブジェクトが自動的に消えてくれるCDIは、システムリソースを無駄にしないからです。
オブジェクトの寿命をCDIシステムが管理する都合から、CDIのクラスはnewで実体化しません。CDIシステムからもらう形をとります。例えば、次はログを取るLoggerクラスをCDIクラスとして定義している場合の例ですが、@Injectというアノテーションを付けるだけで、Loggerクラスのオブジェクトがlogにセットされます。
@Inject
Logge log;
このようなシステムによるオブジェクトの管理と自動配布(もちろん他の言い方もありますが)をDI(依存性注入)といいます。CDIのDIもそれで、Cはコンテキスト、つまりは寿命のことです。ですから、寿命のあるオブジェクトの管理と自動配布がCDIです。なお、作成したオブジェクトをシステム側で管理するのはJPAやEJBでも同じなので、(Cのつかない)DIは、JPAやEJBで使われます。ただし、それに使うアノテーションは@EJBです。
###CDIは王国をつなぐノリ
次の図を見てください。CDIの位置がビミョウですね。
CDIをバッキングビーンとして使うと、JSFにも関係し、データベース処理を実行するEJBにも関係します。JSFやEJBのクラスは、CDIのバッキングビーンと情報を共有するクラスです。つまりは、CDIはそれらをつなぐノリのような役割を果たしているわけです。
##CDIやEJBは難しいことをカンタンにする
ちょこっとアノテーションを付けるだけで、JavaSEの世界では面倒なことがカンタンに実現できます。
この仕組みは本当にスバラシイ限りです。
そこで、CDIとEJBでちょっとだけ例をお話します。
###アッという間にコンカレントな処理
時間のかかる処理をべつのスレッドで実行すれば、本来の処理をすぐに続けることができます。例えば、次は電子メールを送信する処理です。JmSenderというユーティリティクラスのクラスメソッドを使ってメールを送信します。これは、意外と時間がかかる処理ですが、JavaSEだと、別スレッドで実行するには、これにRunnableインタフェースを実装した上で、別にスレッドプールを作成して、その中で実行する必要があります。
public class MailSender {
/* この部分、初期値が必要です。以下のように訂正します。
String mail_id; // メールアカウントID
String mail_pw; // 同パスワード
String from; // 差出人メールアドレス
*/
String mail_id = "あなたのメールアカウントID";
String mail_pw = "あなたのメールパスワード";
String from = "あなたのメールアドレス";
String host = "smtp.gmail.com"; // smtpサーバー
int port = 587; // smtpサーバーポート番号
public void send(String mail, String subject, String body) {
try {
JmSender.send(mail_id, mail_pw, host, port, mail, from, subject, body);
} catch (Exception e) {
}
}
/* 削除します
// セッター、ゲッター(省略)
*/
}
しかし、これをEJBのクラスにすれば、アノテーション1つで別スレッドで実行するクラスに変身できます。
次を見てください。
@Stateless
public class MailSender {
/* この部分、初期値が必要です。以下のように訂正します。
String mail_id; // メールアカウントID
String mail_pw; // 同パスワード
String from; // 差出人メールアドレス
*/
String mail_id = "あなたのメールアカウントID";
String mail_pw = "あなたのメールパスワード";
String from = "あなたのメールアドレス";
String host = "smtp.gmail.com"; // smtpサーバー
int port = 587; // smtpサーバーポート番号
@Asynchronous
public void send(String mail, String subject, String body) {
try {
JmSender.send(mail_id, mail_pw, host, port, mail, from, subject, body);
} catch (Exception e) {
}
}
/* 削除します
// セッター、ゲッター(省略)
*/
}
先頭行の@Statelessは、このクラスをEJBにするというアノテーションです。このアノテーションをつけるだけで、Enterprise JavaBeansになります。そして、8行目にある@Asynchronousアノテーションが、別スレッドで実行するためのアノテーションです。コンカレント処理を行わせるには、このアノテーションを付けるだけで、後はなにも必要ありません。
実行するには、特にスレッドプールを作る必要はなく、そのまま普通に実行するだけです。
ついでに、次がユーザーインタフェースになる画面の定義と、それからデータを受け取ってメール送信するバッキングビーンです。上の(EJBにした)MailSenderクラスを使っていますが、普通に実行しているだけですね。
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>sample18-03</title>
<h:outputStylesheet name="mystyle.css" library="css" />
</h:head>
<h:body>
<h1>非同期処理</h1>
<h:form>
<h:panelGrid columns="2" columnClasses="col">
宛 先<h:inputText value="#{bb.mail}" size="30"/>
題 名<h:inputText value="#{bb.title}" size="40"/>
連絡文<h:inputTextarea value="#{bb.msg}" cols="40" rows="10" />
</h:panelGrid>
<h:commandButton value="送信" action="#{bb.next()}" />
</h:form>
</h:body>
</html>
/* バッキングビーン */
@Named
@RequestScoped
public class Bb {
private String mail; // 送信先メールアドレス
private String title; // メールタイトル
private String msg; // メール本文
@EJB
MailSender sender;
// メールの送信
public String next() {
/* この部分誤りですので削除します。
sender.setMail_id("・・・"); // あなたのメールID(Google mail)
sender.setMail_pw("・・・"); // あなたのメールパスワード
ender.setFrom("・・・"); // あなたのメールアドレス
*/
sender.send(mail, title, msg);
return null;
}
// セッター、ゲッター(省略)
}
※JmSenderなどのメール送信ユーティリティは著者のサポートウェブからダウンロードできます。
JmSenderはJavaMailのラッパーです。メールの連続送信もできます。
http://k-webs.jp
このように、わずなアノテーションだけでマルチスレッドが実行できるのは、EJBの利点のひとつです。
###オブザーバーパターンがカンタンに実現
あるオブジェクトが、何かの処理をしたとき、それを他のオブジェクトに教えたいことがあります。例えば、「学生データを学籍簿に登録する」オブジェクトが、「学籍簿から成績簿を作る」オブジェクトや、「学籍簿から学費の納付書を発行する」オブジェクトなどに、登録した事実を伝える場合です。これらを1つのクラスに作っておくのではなく、バラバラにしておけば、自由度が増し、改訂も容易になります。このような処理をオブザーバーパターンといいます。
CDIやEJBでは、このオブザーバーパターンをカンタンに作れます。
次はCDIのクラスでオブザーバーパターンを実装した例です。
例では、上段の学生課(Gakusei_ka)クラスで、学生情報をリストに登録し(ダミーの処理)、その後、イベントを発火して登録した事実をシステム全体に知らせています。
Eventオブジェクトのfireメソッドがその処理です。引数に登録した学生情報があるので、システムの他のオブジェクトならだれでも、このイベントを捕えることができます。
@SessionScoped
public class Gakusei_ka implements Serializable {
@Inject
private Event<Student> event; // イベントオブジェクトを用意しておく
List<Student> ls = new ArrayList<>();
public void add(Integer number, String name) {
Student st = new Student(number, name); // 学生データを学籍簿に登録
ls.add(st);
event.fire(st); // 引数(Student型)をセットしてイベントを発火させる
}
}
次は、イベントを捕える側のクラスです。引数に@Observesアノテーションを付けたStudentオブジェクトを持つ、receiveメソッドを作っておけば、イベントが投げられた時、リアルタイムにそれを捕えることができます。ここでは、ダミーの処理として、メッセージを出力しています。
このsoumu_kaクラス以外にも、同形式のreceiveメソッドを持つクラスは、すべて、イベントを捕えることができます。
@RequestScoped
public class Soumu_ka {
public void receive(@Observes Student st) {
System.out.println("■納付書発行処理:" + st.toString());
}
}
#終わりに
JavaEEのすばらしさ、少しでも伝わったでしょうか?
JavaEEを使うと、実用的なシステムが、比較的簡単に、そして、すばやく構築できます。
あなたもやってみませんか。
Javaによるウェブシステムが初めてなら、「わかりやすいJavaEE」を手に取ってみてください。
基礎知識だけでなく、実践的なシステム作成技術も解説しています。
Java言語の初心者でも読めるように書きましたので、きっと役に立つと思います!
----------------------------
2014.12.10 川場隆
http://k-webs.jp
kawaba[アット]k-webs.jp(@に読み替えてください)