イベント伝播(Spring1.2.1)
ApplicationContextでは,イベントを伝播する仕組みがあるらしいので,試してみた.
ApplicationContextには,イベントを伝播するためのメソッドが用意されている.
void publishEvent(ApplicationEvent event)このメソッドでApplicationContextは,beans.xmlに記述されたbeanの中から,org.springframework.context.ApplicationListenerの実装クラスを探し,以下のメソッド呼び出す.
void onApplicationEvent(ApplicationEvent event)引数のeventは,publishEventの引数と一致する. ApplicationEventは,Spring組み込みのものを使用できるし,自作したものも使用できる. Spring組み込みで,かつ自動的にpublishEventされるものとして以下がある.
- org.springframework.context.event.ContextRefreshedEvent
- ApplicationContextが初期化あるいはリフレッシュされた際にpublishされる.
- org.springframework.context.event.ContextClosedEvent
- ApplicationContextがcloseされた時にpublishされる.
以下にサンプル用に独自に作成したApplicationEventを示す.
ApplicationEventを拡張する点以外,特に制限はない.
各アプリケーションでの必要に応じて,随時何かを追加する.
本頁のサンプルでは,ApplicationListenerを二つ作成した.
まず一つ目.
このクラスはSpring組み込みで自動的にpublishされるイベントを処理している.
ここではinstanceofにてイベントのクラスを特定し,特定結果に応じた処理をしている.
どのイベントが発生しても,全てのリスナが呼び出されるので(※),こうした処理が必要である.
また,上記のようにApplicationEventには,publishした時刻とオブジェクトを返すメソッドがある.
このクラスは特にサンプル独自のイベントを処理している.
やはりinstanceofにてイベントクラスを特定しているが,instanceofはリソースを多く消費するという話しもあるので,独自のイベントについてはinstanceofで分類後,何か独自の識別子(サンプルではgetNameで取得できる値)にて処理を分岐させるなどした方がよいかもしれない.
long getTimestamp() Object getSource()次に二つ目.
※ しかもブロッキング呼び出しなので,リスナの数や処理時間に注意する必要がある場合がある.
以下に本頁のサンプルのbeans.xmlを以下に示す.
ApplicationContextにApplicationListenerを知らせるために,これらのbeanを登録する.
また,SampleBeanも作成し,登録した.
サンプルでは,このbeanからイベントをpublishするが,特にそういった制限があってやっているわけではない.
サンプルドライバから直接publishEventメソッドを呼び出してもよい.
以下にSampleBeanのソースを示す.
メソッドnotifySampleEventにてイベントをpublishしている.
public void notifySampleEvent() {
logger.info("========== publish event1 ==========");
myContext.publishEvent(new SampleEvent(this, "event1"));
logger.info("========== publish event2 ==========");
myContext.publishEvent(new SampleEvent(this, "event2"));
}
ApplicationContextであるmyContextは,setApplicationContextにて設定される.
このsetApplictionContextメソッドは,SampleBeanがorg.springframework.context.ApplicationContextAwareを実装していることによって,ApplicationContextから自動的に呼び出される(※).
※ org.springframework.beans.factory.BeanFactoryAwareと同様.
サンプルのドライバクラスを以下に示す.
まずApplicationContextを作成,refreshする.
次にSampleBeanを取得してイベントのpublishする.
最後にApplicationContextをcloseする.
では実行してみる.
$> java -jar springsample.jar
...省略...
support.AbstractApplicationContext:444
- Unable to locate ApplicationEventMulticaster with name
'applicationEventMulticaster': using default
[org.springframework.context.event.SimpleApplicationEventMulticaster@20be79]
support.AbstractBeanFactory:219
- Creating shared instance of singleton bean 'sampleEventListener2'
support.AbstractBeanFactory:219
- Creating shared instance of singleton bean 'sampleEventListener1'
support.DefaultListableBeanFactory:262
- Pre-instantiating singletons in factory
[org.springframework.beans.factory.xml.XmlBeanFactory defining beans
[sampleEventListener2,sampleEventListener1,sampleBean]; root of BeanFactory hierarchy]
support.AbstractBeanFactory:219 - Creating shared instance of singleton bean 'sampleBean'
springsample.SampleEventListener1:20 - ContextRefreshedEvent
springsample.SampleEventListener1:26 - Mon Jul 18 11:06:30 JST 2005
springsample.SampleEventListener1:27
- org.springframework.context.support.GenericApplicationContext: display name
[org.springframework.context.support.GenericApplicationContext;hashCode=7804298];
startup date [Mon Jul 18 11:06:30 JST 2005]; root of context hierarchy
springsample.SampleEventListener2:23 - else event
springsample.SampleEventListener2:25 - Mon Jul 18 11:06:30 JST 2005
springsample.SampleEventListener2:26
- org.springframework.context.support.GenericApplicationContext: display name
[org.springframework.context.support.GenericApplicationContext;hashCode=7804298];
startup date [Mon Jul 18 11:06:30 JST 2005]; root of context hierarchy
springsample.SampleBean:21 - ========== publish event1 ==========
springsample.SampleEventListener1:24 - else event
springsample.SampleEventListener1:26 - Mon Jul 18 11:06:30 JST 2005
springsample.SampleEventListener1:27 - org.fireproject.springsample.SampleBean@21b220
springsample.SampleEventListener2:21 - event1
springsample.SampleEventListener2:25 - Mon Jul 18 11:06:30 JST 2005
springsample.SampleEventListener2:26 - org.fireproject.springsample.SampleBean@21b220
springsample.SampleBean:23 - ========== publish event2 ==========
springsample.SampleEventListener1:24 - else event
springsample.SampleEventListener1:26 - Mon Jul 18 11:06:30 JST 2005
springsample.SampleEventListener1:27 - org.fireproject.springsample.SampleBean@21b220
springsample.SampleEventListener2:21 - event2
springsample.SampleEventListener2:25 - Mon Jul 18 11:06:30 JST 2005
springsample.SampleEventListener2:26 - org.fireproject.springsample.SampleBean@21b220
springsample.HelloApplicationContext:38 - ========== context close ==========
support.AbstractApplicationContext:499
- Closing application context
[org.springframework.context.support.GenericApplicationContext;hashCode=7804298]
springsample.SampleEventListener1:22 - ContextClosedEvent
springsample.SampleEventListener1:26 - Mon Jul 18 11:06:30 JST 2005
springsample.SampleEventListener1:27
- org.springframework.context.support.GenericApplicationContext: display name
[org.springframework.context.support.GenericApplicationContext;hashCode=7804298];
startup date [Mon Jul 18 11:06:30 JST 2005]; root of context hierarchy
springsample.SampleEventListener2:23 - else event
springsample.SampleEventListener2:25 - Mon Jul 18 11:06:30 JST 2005
springsample.SampleEventListener2:26
- org.springframework.context.support.GenericApplicationContext: display name
[org.springframework.context.support.GenericApplicationContext;hashCode=7804298];
startup date [Mon Jul 18 11:06:30 JST 2005]; root of context hierarchy
support.AbstractBeanFactory:525 - Destroying singletons in factory
{org.springframework.beans.factory.xml.XmlBeanFactory defining beans
[sampleEventListener2,sampleEventListener1,sampleBean]; root of BeanFactory hierarchy}
リスナの呼び出される順番がbeans.xmlの記述した順番と一致していないことに注意.
本家ドキュメントにも特に記述が見当たらないので,リスナの呼び出し順に関する制約は設けないほうが無難だろうか.

