3.2.6.4. Events

Бин Events реализует функциональность публикации объектов-событий уровня приложения. События могут использоваться для передачи данных между слабо связанными компонентами приложения. Бин Events является простым фасадом для объекта ApplicationEventPublisher Spring Framework.

public interface Events {
    String NAME = "cuba_Events";

    void publish(ApplicationEvent event);
}

Этот бин имеет только один метод - publish(), принимающий объект события. Метод Events.publish() уведомляет все слушатели, зарегистрированные в приложении и подписанные на события того же типа, что и переданный объект. Вы может использовать класс-обёртку PayloadApplicationEvent для публикации любых объектов в качестве событий.

Обработка событий в компонентах приложения

Прежде всего, необходимо создать класс события. Он должен быть наследником класса ApplicationEvent. Класс события может включать любые дополнительные данные. Например:

package com.company.sales.core;

import com.haulmont.cuba.security.entity.User;
import org.springframework.context.ApplicationEvent;

public class DemoEvent extends ApplicationEvent {

    private User user;

    public DemoEvent(Object source, User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

Бины могут публиковать события, используя бин Events:

package com.company.sales.core;

import com.haulmont.cuba.core.global.Events;
import com.haulmont.cuba.core.global.UserSessionSource;
import com.haulmont.cuba.security.global.UserSession;
import org.springframework.stereotype.Component;
import javax.inject.Inject;

@Component
public class DemoBean {

    @Inject
    private Events events;
    @Inject
    private UserSessionSource userSessionSource;

    public void demo() {
        UserSession userSession = userSessionSource.getUserSession();
        events.publish(new DemoEvent(this, userSession.getUser()));
    }
}

По умолчанию все события обрабатываются синхронно.

Есть два способа обработки событий:

  • Реализовать интерфейс ApplicationListener.

  • Использовать аннотацию @EventListener для метода.

В первом случае, мы должны создать бин, реализующий интерфейс ApplicationListener с указанием типа события:

@Component
public class DemoEventListener implements ApplicationListener<DemoEvent> {
    @Inject
    private Logger log;

    @Override
    public void onApplicationEvent(DemoEvent event) {
        log.debug("Demo event is published");
    }
}

Второй способ может использоваться для сокрытия деталей реализации обработчика событий и обработки множества различных событий в одном бине:

@Component
public class MultipleEventListener {
    @Order(10)
    @EventListener
    protected void handleDemoEvent(DemoEvent event) {
        // handle event
    }

    @Order(1010)
    @EventListener
    protected void handleUserLoginEvent(UserLoggedInEvent event) {
        // handle event
    }
}

По умолчанию, события в Spring требуют модификаторов доступа protected, package или public для методов, аннотированных @EventListener. Обратите внимание, что модификатор private не поддерживается.

Методы, аннотированные @EventListener, не работают в services, JMX-бинах и других бинах с интерфейсами. При использовании @EventListener в таком бине на старте приложения будет выброшено исключение:

BeanInitializationException: Failed to process @EventListener annotation on bean. Need to invoke method declared on target class, but not found in any interface(s) of the exposed proxy type. Either pull the method up to an interface or switch to CGLIB proxies by enforcing proxy-target-class mode in your configuration.

Если необходимо обрабатывать событие в бине с интерфейсом, реализуйте в нем также интерфейс ApplicationListener с типом нужного события.

Вы можете использовать интерфейс Ordered и аннотацию @Order Spring Framework для указания порядка исполнения обработчиков событий. Все бины и обработчики событий платформы используют значение order от 100 до 1000, таким образом, вы можете добавить обработчик событий как до, так и после обработчиков события платформы. Если вы хотите добавить свой обработчик события до обработчиков из платформы, то используйте значение меньше 100.

См. также События логина.

Обработка событий в экранах

Обычно, бин Events делегирует публикацию события объекту ApplicationContext. Для блока Web Client это поведение отличается от стандартного, вы можете использовать дополнительный интерфейс для классов событий - UiEvent. Это интерфейс-маркер для событий, которые должны быть доставлены в экраны пользовательского интерфейса текущего экземпляра UI (текущей вкладки веб-браузера). Важно отметить, что экземпляры событий, реализующих UiEvent, не доставляются в бины Spring и не могут быть обработаны за пределами UI.

Пример класса события:

package com.company.sales.web;

import com.haulmont.cuba.gui.events.UiEvent;
import com.haulmont.cuba.security.entity.User;
import org.springframework.context.ApplicationEvent;

public class UserRemovedEvent extends ApplicationEvent implements UiEvent {

    private User user;

    public UserRemovedEvent(Object source, User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

События публикуются при помощи бина Events из контроллера экрана так же, как и из бинов Spring:

@Inject
Events events;
// ...
UserRemovedEvent event = new UserRemovedEvent(this, removedUser);
events.publish(event);

Чтобы обработать событие, вы должны объявить в экране метод с аннотацией @EventListener (Интерфейс ApplicationListener не поддерживается):

@Order(15)
@EventListener
protected void onUserRemove(UserRemovedEvent event) {
    notifications.create()
            .withCaption("User is removed " + event.getUser())
            .show();
}

Вы можете использовать аннотацию @Order, чтобы задать порядок вызова обработчиков события.

Если класс события реализует UiEvent, и объект такого события опубликован при помощи бина Events из потока UI, то будут вызваны обработчики событий этого типа в открытых на данный момент окнах и фреймах. Обработка событий синхронная. Только экраны и фреймы текущей активной вкладки веб-браузера получат уведомление о событии.