LoginSignup
18
12

More than 5 years have passed since last update.

CDIのEventについてメモ

Last updated at Posted at 2013-09-01

CDI の Event についてメモ。
要は、 Observer パターン的なものを CDI の機能を使って実現する方法のこと。

環境

Weld

1.1.10

基本的な実装

通知する人(Subject)

Subject.java
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)

ObserverA.java
package cdi.event.nullpo;

import javax.enterprise.event.Observes;

public class ObserverA {
    public void event(@Observes NullPo nullPo) {
        System.out.println("[Observer A] ガッ!!");
    }
}
ObserverB.java
package cdi.event.nullpo;

public class ObserverB {
    public void event(NullPo nullPo) {
        System.out.println("[Observer B] ガッ!!");
    }
}
ObserverC.java
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] ガッ!!");
    }
}

情報をやり取りするためのクラス

NullPo.java
package cdi.event.nullpo;

public class NullPo {
}

実行用クラス

Main.java
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 アノテーションが付与されている全てのメソッドにイベントが通知される。

Observers
// 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)

Subject.java
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)

Taro.java
package cdi.event.qualifier;

import javax.enterprise.event.Observes;

public class Taro {
    public void event(@Observes @Man String info) {
        System.out.println("[太郎] ノ");
    }
}
Hanako.java
package cdi.event.qualifier;

import javax.enterprise.event.Observes;

public class Hanako {
    public void event(@Observes @Woman String info) {
        System.out.println("[花子] ノ");
    }
}
OsugiAndPiko
package cdi.event.qualifier;

import javax.enterprise.event.Observes;

public class OsugiAndPiko {
    public void event(@Observes String info) {
        System.out.println("[おすピー] ノ");
    }
}

カスタム限定子

Man.java
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 {
}
Woman.java
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 {
}

実行用クラス

Main.java
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] 女性挙手
[花子] ノ
[おすピー] ノ

説明

Eventにカスタム限定子を付与して、通知対象を絞り込む
    @Inject @Man
    private Event<String> manEvent;

    @Inject @Woman
    private Event<String> womanEvent;

Event をインジェクションする場所でカスタム限定子を付与することで、そのイベントの通知先を絞り込むことが出来る。

カスタム限定子で絞りこまれたイベントは、 @Observers アノテーションのみ付与されているか、または @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 メソッドで例外をキャッチできる。

Main.java
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!!

参考

18
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
12