08 апреля 2019

Java ЕЕ предоставляет нам хороший механизм для обработки событий, который является частью CDI для спецификации Java EE. Динамический CDI спецификатор может быть полезен для обработки событий, например, в domain driven архитектуре или при маршрутизации сообщений веб-сокета и т.д.

Генерация простого события:

@Named
public class MyEventSource {

    @Inject
    private Event<String> myEvent;

    public void fireEvent(){
        myEvent.fire("Hello World!");
    }
}

Наблюдатель события:

@Named
public class MyEventObserver {
    public void observeEvent(@Observes String message){
        System.out.println(message);
    }
}

Используя CDI спецификатор, можно определить, какой наблюдатель должен обработать событие

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Important {
}

Например:

@Named
public class MyEventSource {

    @Inject
    @Important
    private Event<String> myEvent;
    ...
@Named
public class MyEventObserver {
  public void observeEvent(@Observes @Important String message){
    ...

По-умолчанию, событие будет обработано обсервером в текущей транзакции, но можно изменить это поведение, используя @Observes атрибут during

@Named
public class TransactionEventObserver {
    public void observeImportantMessage(@Observes(during = TransactionPhase.AFTER_SUCCESS) String message){
        System.out.println(message);
    }
}

Доступны следующие значения:

Теперь давайте посмотрим как можно квалифицировать CDI события динамически. В примере ниже мы создадим обсервер для обработки пользовательских событий (вход в систему, выход, регистрация и т.д.), полученных из некого абстрактного источника.

Итак, сначала нам нужно создать Qualifier с доступными значениями событий

@Qualifier
@Target({METHOD, FIELD, PARAMETER, TYPE})
@Retention(RUNTIME)
public @interface UserEvent {

    Routes value();
}

где Routes - это enum с доступными значениями, например:

public enum Routes {
  LOGIN,
  LOGOUT,
  REGISTRATION
}

Потом нам нужно создать дочерний от javax.enterprise.util.AnnotationLiteral класс для возможности использования квалификатора динамически.

public class UserEventBinding extends AnnotationLiteral<UserEvent> implements UserEvent {

    Routes routes;

    public UserEventBinding(Routes routes) {
        this.routes = routes;
    }

    @Override
    public Routes value() {
        return routes;
    }
}

Теперь давайте сгенерируем событие, используя динамический выбор наблюдателей

@Named
public class UserEventSource {

    @Inject
    private Event<String> userEvent;

    public void fireEvent(Routes route){
        userEvent.select(new UserEventBinding(route)).fire("Instead of string you can use your object");
    }
}

Время показать, как выглядит наш Observer.

import static Routes.*;
...

@Named
public class UserObserver {

    public void registration(@Observes @UserEvent(REGISTRATION) String eventData) {
      ....
    }

    public void login(@Observes @UserEvent(LOGIN) String eventData) {
      ....
    }

    public void logout(@Observes @UserEvent(LOGOUT) String eventData) {
      ....
    }
}

P.S. В Java EE 8 с CDI 2.0 вы можете использовать асинхронные события CDI с помощью метода fireAsync и аннотации @ObserveAsync