Spring Framework 入門記 Contextその3 イベント
今回は「3.10.2. Propagating events」です.
ApplicationContext
はイベントを通知してくれるそうです.このイベントはApplicationEvent
です.ということは当然,このイベントを受け取るにはApplicationListener
をimplements
するわけですね.
では,このApplicationEvent
がどんなイベントなのかというと,タイムスタンプを持つだけの超単純な代物なんですね.これは,汎用のイベントメカニズムのためのものらしいです.つまり,アプリは必要に応じてApplicationEvent
をextends
して独自のイベントを作成していいよってことみたい.
ということは,アプリからイベントが発生したことを通知できなきゃいけないわけですが,そのためにApplicationContext
にはpublishEvent(ApplicationEvent)
というメソッドが用意されています.こいつを呼び出すと,ApplicationContext
はリスナーにイベントを配信してくれるようです.
ちなみに,Spring Framework自身が用意しているイベントもあります.
ContextRefreshedEvent
ApplicationContext
が初期化されたり,リフレッシュ(!)されたことを通知します.ContextClosedEvent
ApplicationContext
がクローズされたことを通知します.RequestHandledEvent
- Spring Frameworkの
DispatcherServlet
が使用するもので,HTTPリクエストが処理されたことを通知します.
何気にこんなところでリフレッシュとか出てくるし.もっとまともに取り上げて解説してくれてもいいと思うんですけど.重要な機能ですよねぇ?
まぁ,いずれ解説があることに期待しつつ,なければ実験するってことで気を取り直していきましょう.
イベントを受け取るにはApplicationListener
をimplements
すればいいわけですが,ApplicationContext
にはaddApplicationListener
なんてメソッドがあるわけではありません.ApplicationContext
君はApplicationListener
をimplements
しているBean君には,片っ端から通知してくれるようです.ちょっと乱暴者?
だいたいのことは分かったので,さっそく実験.
まずはイベントリスナーを作ります.
package study; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextClosedEvent; public class Zaizen implements ApplicationListener { public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextClosedEvent) { System.out.println("無念だ."); } } }
ここでは,ContextClosedEvent
だけを処理しています.
次にBeanの定義ファイル.プロパティも何も無いので超簡単です(<bean>
要素のみ抜粋).
<bean name="zaizen" class="study.Zaizen"> </bean>
最後に実行用のクラス.これも単純にApplicationContext
を作ってclose()
するだけです.
package study; import org.springframework.context.support.FileSystemXmlApplicationContext; public class Main { public static void main(String args) { try { FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext( new String { "factory.xml", "beans.xml" }); context.close(); } catch (Exception e) { e.printStackTrace(); } } }
これを実行すると...
無念だ.
とちゃんと出力されました.このクラス,なにもしていないのですからさぞかし無念でしょう.
ところで,この例ではzaizen
教授はsingletonです(<bean>
要素で明示していないため,デフォルトでそうなります).果たしてprototypeではどうなるのでしょう? ちょっと疑問.
そんな場合は実験です.定義ファイルでprototypeを指定します.
<bean name="zaizen" class="study.Zaizen" singleton="false"> </bean>
これで実行すると...
無念だ.
同じですねー.ちょっと意外.イベントは届かなくなるんじゃないかと予想したんですが.今の場合,ApplicationContext
からは何もgetBean()
していないので,close()
する前にgetBean()
してみます.
context.getBean("zaizen");
これで実行しても,結果は同じでした.getBean()
をいくら増やしても同じ,きっちり1回だけイベントが通知されます.調べてみると,イベントを受け取るインスタンスはgetBean()
で返ってきたどのインスタンスとも別であることが分かりました.どうやら,prototypeの雛型になっているインスタンスに対して通知されるようです.ちなみにsingletonの場合は,最初からインスタンスが1つしかないので,getBean()
で返ってくるのと同じインスタンスに通知されます.覚えておきましょう(それが難しい!).
大体こんなところでしょうか.独自のイベントを作ることもちょっとだけ考えましたが,そんなに使う機会があるとも思えないし,そういうこともできるってことを忘れないようにする(これが難しい!)ってことでこの節を終わりにします.
ところですっかり忘れていましたが,かつてJavaBeansといえばマイクロソフトのVBX(古い?)やOCXに対応するGUIのためのコンポーネント(ウィジェット)というイメージだった時期がありましたよね.その当時JavaBeansといえばプロパティ共々イベントが必ず解説されていて,JavaBeansに対応したGUI構築ツール(ポトリペタペタ系)の中にはイベントをグリグリッと配線できたものもありました.VisualAgeとか.
でも,Spring FrameworkはイベントソースであるBeanとイベントリスナーであるBeanをつないでくれたりはしないんですね.サーバサイドだとスレッドやトランザクションなどが絡んできて,GUIまわりに比べてObserverパターンを使いにくいということが一因でしょうか.あるいはAspectがその代替手段として使えるからでしょうか.Springは,PropertyEditor
などJavaBeansを徹底活用の印象があっただけに,ちょっと意外な感じがしました.