CDI の Event についてメモ。
要は、 Observer パターン的なものを CDI の機能を使って実現する方法のこと。
#環境
##Weld
1.1.10
#基本的な実装
##通知する人(Subject)
package cdi.event.nullpo;
import javax.enterprise.event.Event;
import javax.inject.Inject;
public class Subject {
@Inject private Event<NullPo> event;
public void notifyObservers() {
System.out.println("[Subject] ぬるぽ");
this.event.fire(new NullPo());
}
}
##通知を受ける人(Observer)
package cdi.event.nullpo;
import javax.enterprise.event.Observes;
public class ObserverA {
public void event(@Observes NullPo nullPo) {
System.out.println("[Observer A] ガッ!!");
}
}
package cdi.event.nullpo;
public class ObserverB {
public void event(NullPo nullPo) {
System.out.println("[Observer B] ガッ!!");
}
}
package cdi.event.nullpo;
import java.util.List;
import javax.enterprise.event.Observes;
public class ObserverC {
public void event(@Observes List<String> list) {
System.out.println("[Observer C] ガッ!!");
}
}
##情報をやり取りするためのクラス
package cdi.event.nullpo;
public class NullPo {
}
##実行用クラス
package cdi.event.nullpo;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
public class Main {
public static void main(String[] args) {
Weld weld = new Weld();
WeldContainer container = weld.initialize();
Subject application = container.instance().select(Subject.class).get();
application.notifyObservers();
weld.shutdown();
}
}
##実行結果
[Subject] ぬるぽ
[Observer A] ガッ!!
##説明
import javax.enterprise.event.Event;
import javax.inject.Inject;
public class Subject {
@Inject private Event<NullPo> event; // Event オブジェクトを取得して
public void notifyObservers() {
this.event.fire(new NullPo()); // fire メソッドでイベントを通知
}
}
イベントの通知は javax.enterprise.event.Event
クラスの fire
メソッドを使用する。 Event
オブジェクト自体は、 @Inject
を使ってインジェクションしておく。
fire
メソッドの引数に渡したクラスを引数に受け取るメソッドで、かつその引数に javax.enterprise.event.Observes
アノテーションが付与されている全てのメソッドにイベントが通知される。
// Observer A
// NullPo を受け取り、 @Observer アノテーションが付与されているので通知対象
public void event(@Observes NullPo nullPo) {
System.out.println("[Observer A] ガッ!!");
}
// Observer B
// NullPo を受け取っているが、 @Observer アノテーションが付与されていないので、通知対象外
public void event(NullPo nullPo) {
System.out.println("[Observer B] ガッ!!");
}
// Observer C
// NullPo を受け取らないので、通知対象外
public void event(@Observes List<String> list) {
System.out.println("[Observer C] ガッ!!");
}
#カスタム限定子で通知対象を絞り込む
カスタム限定子を使って、通知対象を絞り込める。
##通知する人(Subject)
package cdi.event.qualifier;
import javax.enterprise.event.Event;
import javax.inject.Inject;
public class Subject {
@Inject @Man
private Event<String> manEvent;
@Inject @Woman
private Event<String> womanEvent;
public void notifyObservers() {
System.out.println("[Subject] 男性挙手");
this.manEvent.fire("notify");
System.out.println("\r\n[Subject] 女性挙手");
this.womanEvent.fire("nodify");
}
}
##通知を受ける人(Observer)
package cdi.event.qualifier;
import javax.enterprise.event.Observes;
public class Taro {
public void event(@Observes @Man String info) {
System.out.println("[太郎] ノ");
}
}
package cdi.event.qualifier;
import javax.enterprise.event.Observes;
public class Hanako {
public void event(@Observes @Woman String info) {
System.out.println("[花子] ノ");
}
}
package cdi.event.qualifier;
import javax.enterprise.event.Observes;
public class OsugiAndPiko {
public void event(@Observes String info) {
System.out.println("[おすピー] ノ");
}
}
##カスタム限定子
package cdi.event.qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface Man {
}
package cdi.event.qualifier;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface Woman {
}
##実行用クラス
package cdi.event.qualifier;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
public class Main {
public static void main(String[] args) {
Weld weld = new Weld();
WeldContainer container = weld.initialize();
Subject application = container.instance().select(Subject.class).get();
application.notifyObservers();
weld.shutdown();
}
}
##実行結果
[Subject] 男性挙手
[太郎] ノ
[おすピー] ノ
[Subject] 女性挙手
[花子] ノ
[おすピー] ノ
##説明
@Inject @Man
private Event<String> manEvent;
@Inject @Woman
private Event<String> womanEvent;
Event
をインジェクションする場所でカスタム限定子を付与することで、そのイベントの通知先を絞り込むことが出来る。
カスタム限定子で絞りこまれたイベントは、 @Observers
アノテーションのみ付与されているか、または @Observers
アノテーションとカスタム限定子が両方ともパラメータに付与されているメソッドに通知される。
// Taro
// @Man が付与された Event だけが通知される
public void event(@Observes @Man String info) {
System.out.println("[太郎] ノ");
}
// Hanako
// @Woman が付与された Event だけが通知される
public void event(@Observes @Woman String info) {
System.out.println("[花子] ノ");
}
// OsugiAndPiko
// カスタム限定子が付与されていないので、全ての Event が通知される
public void event(@Observes String info) {
System.out.println("[おすピー] ノ");
}
ちなみに、 @Man
と @Woman
の両方が付与されていると、逆にイベントは通知されない。
public void event(@Observes @Man @Woman String info) {
System.out.println("ノ");
}
##カスタム限定子を動的に指定する
前述の方法だと、イベントの絞り込みが静的になり、条件によって通知する対象を切り替えるような動的な処理ができない。
動的に通知対象を絞り込みたい場合は以下のようにする。
@Inject
private Event<String> event;
@SuppressWarnings("serial")
public void notifyObservers() {
this.event.select(new AnnotationLiteral<Man>() {}).fire("notify");
}
Event#select(Annotation...)
メソッドで絞込対象を指定して、続けて fire
メソッドを実行する。
#例外処理について
Observer 側のいずれかの処理で例外が発生した場合は、 fire
メソッドで例外をキャッチできる。
package cdi.event.test;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
public class Main {
public static void main(String[] args) {
Weld weld = new Weld();
WeldContainer container = weld.initialize();
Main main = container.instance().select(Main.class).get();
main.notifyObservers(); // 1
weld.shutdown();
}
@Inject
private Event<String> event;
public void notifyObservers() {
try {
this.event.fire("notify"); // 2
} catch (Exception e) {
System.out.println(e.getMessage()); // 4
}
}
public void event(@Observes String info) {
throw new RuntimeException("error!!"); // 3
}
}
error!!
#参考