Гуава EventBus
В библиотеке Google Guava есть полезный пакет eventbus . Класс EventBus позволяет осуществлять связь между компонентами в стиле публикации-подписки, не требуя, чтобы компоненты явно регистрировались друг с другом. Поскольку мы разрабатываем веб-приложения, мы должны инкапсулировать экземпляр этого класса в bean-объект.
Давайте напишем бин EventBusProvider.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class EventBusProvider implements Serializable { private EventBus eventBus = new EventBus("scopedEventBus"); public static EventBus getEventBus() { // access EventBusProvider bean ELContext elContext = FacesContext.getCurrentInstance().getELContext(); EventBusProvider eventBusProvider = (EventBusProvider) elContext.getELResolver().getValue(elContext, null, "eventBusProvider"); return eventBusProvider.eventBus; }} |
Я хотел бы продемонстрировать все основные функции Guava EventBus только в одном примере. Давайте напишем следующую иерархию событий:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class SettingsChangeEvent {}public class LocaleChangeEvent extends SettingsChangeEvent { public LocaleChangeEvent(Object newLocale) { ... }}public class TimeZoneChangeEvent extends SettingsChangeEvent { public TimeZoneChangeEvent(Object newTimeZone) { ... }} |
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public MyBean1 implements Serializable { @PostConstruct public void initialize() throws Exception { EventBusProvider.getEventBus().register(this); } @Subscribe public void handleLocaleChange(LocaleChangeEvent event) { // do something } @Subscribe public void handleTimeZoneChange(TimeZoneChangeEvent event) { // do something }}public MyBean2 implements Serializable { @PostConstruct public void initialize() throws Exception { EventBusProvider.getEventBus().register(this); } @Subscribe public void handleSettingsChange(SettingsChangeEvent event) { // do something }} |
Чтобы опубликовать событие, просто предоставьте объект события методу post () экземпляра EventBus. Экземпляр EventBus определит тип события и направит его всем зарегистрированным слушателям.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class UserSettingsForm implements Serializable { private boolean changed; public void localeChangeListener(ValueChangeEvent e) { changed = true; // notify subscribers EventBusProvider.getEventBus().post(new LocaleChangeEvent(e.getNewValue())); } public void timeZoneChangeListener(ValueChangeEvent e) { changed = true; // notify subscribers EventBusProvider.getEventBus().post(new TimeZoneChangeEvent(e.getNewValue())); } public String saveUserSettings() { ... if (changed) { // notify subscribers EventBusProvider.getEventBus().post(new SettingsChangeEvent()); return "home"; } }} |
- Производители событий и наблюдатели событий отделены друг от друга.
- Наблюдатели могут указать комбинацию «селекторов», чтобы сузить набор уведомлений о событиях, которые они будут получать.
- Наблюдатели могут быть уведомлены немедленно или с задержкой до конца текущей транзакции.
- Отсутствие головной боли при определении объема с помощью методов условного наблюдателя (помните проблему с областями действия и Mediator / EventBus?)
|
1
2
3
4
5
6
|
public MyBean implements Serializable { public void onLocaleChangeEvent(@Observes Locale locale) { ... }} |
Параметр события может также указывать квалификаторы, если метод наблюдателя заинтересован только в квалифицированных событиях — это события, которые имеют эти квалификаторы.
|
1
2
3
|
public void onLocaleChangeEvent(@Observes @Updated Locale locale) { ...} |
Спецификатор события — это обычный классификатор, определенный с помощью @Qualifier. Вот пример:
|
1
2
3
4
|
@Qualifier@Target({FIELD, PARAMETER})@Retention(RUNTIME)public @interface Updated {} |
Производители событий запускают события, используя экземпляр параметризованного интерфейса Event. Экземпляр этого интерфейса получается путем инъекции. Производитель вызывает события, вызывая метод fire () интерфейса Event, передавая объект события.
|
1
2
3
4
5
6
7
8
9
|
public class UserSettingsForm implements Serializable { @Inject @Any Event<Locale> localeEvent; public void localeChangeListener(ValueChangeEvent e) { // notify all observers localeEvent.fire((Locale)e.getNewValue()); }} |
|
1
2
|
// this will raise events to observers having parameter @Observes @Updated Locale@Inject @Updated Event<Locale> localeEvent; |
|
1
2
3
|
public void onLocaleChangeEvent(@Observes @Updated Locale locale, User user) { ...} |
Как насчет динамического определения спецификатора? CDI позволяет получить правильный экземпляр квалификатора с помощью AnnotationLiteral. Таким образом, мы можем передать классификатор методу select () класса Event. Пример:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
public class DocumentController implements Serializable { Document document; @Inject @Updated @Deleted Event<Document> documentEvent; public void updateDocument() { ... // notify observers with @Updated annotation documentEvent.select(new AnnotationLiteral<Updated>(){}).fire(document); } public void deleteDocument() { ... // notify observers with @Deleted annotation documentEvent.select(new AnnotationLiteral<Deleted>(){}).fire(document); }} |
|
1
2
3
|
public void onLocaleChangeEvent(@Observes(receive = IF_EXISTS) @Updated Locale locale) { ...} |