Работа с событиями в CDI

Предоставлено Энди Гибсоном (Andy Gibson)

stamp graphic showing page content applies to IDE NetBeans 7.1, 7.2 and 7.3

Внедрение контекстов и зависимостей (CDI), определяемое документом JSR-299, является неотъемлемой частью Java EE 6 и обеспечивает архитектуру, позволяющую компонентам Java EE (например, сервлетам, компонентам EJB и JavaBeans) существовать в жизненном цикле приложения с четко определенными контекстами. Кроме того, службы CDI позволяют компонентам Java EE (например, компонентам сеансов EJB и управляемым компонентам JavaServer Faces) внедряться и свободно взаимодействовать путем запуска и обработки событий.

Этот учебный курс основан на записи блога Энди Гибсона (Andy Gibson) Начало работы с CDI. Часть 3. События. Здесь рассмотрены способы использования концепции событий в Java EE, с помощью которой производят возникающие в приложении события и подписываются на них (т.е. наблюдают) таким образом, что это позволяет управлять несвязанным кодом между производителями и наблюдателями. Класс javax.enterprise.event.Event применяют для создания событий, а аннотацию @Observes CDI — для подписки на события.

В NetBeans IDE обеспечена встроенная поддержка для внедрения контекстов и зависимостей, включая поддержку создания файла конфигурации CDI beans.xml при создании проекта, поддержку редактора и навигации для аннотаций, а также различных мастеров для создания часто используемых артефактов CD.


Для работы с этим учебным курсом требуется программное обеспечение и материалы, перечисленные ниже.

Программное обеспечение или материал Требуемая версия
IDE NetBeans Версия 7.1, 7.2 или 7.3 Java EE
Комплект для разработчика на языке Java (JDK) версия 6
Сервер GlassFish Open Source Edition 3.1 или 3.0.1
cdiDemo3.zip неприменимо

Примечания

  • The IDE NetBeans Java EE bundle also includes the GlassFish Server Open Source Edition 3.x, which is a Java EE 6-compliant container.
  • Поддержка CDI также доступна в NetBeans 6.8 после установки Исправления 1.
  • Демонстрационный проект решения для этого учебного курса можно загрузить здесь: cdiDemoComplete.zip

Использование событий

В предыдущем учебном курсе Применение компонентов @Alternative и аннотаций жизненного цикла у нас было приложение, в котором был получен список элементов, они проверялись, и было выполнено соответствующее действие при обнаружении недопустимого элемента. Предположим, в будущем нам потребуется расширить систему для обработки всевозможных ситуаций, которые происходят при обнаружении недопустимого элемента. Такими ситуациями могут быть отправка электронной почты, изменения других данных, например, отмена заказа, сохранение списка отклонений в файле или таблице базы данных. Для полного развязывания реализации можно использовать события в Java EE. События создаются производителем, а подписка на них выполняется наблюдателем событий. Как и практически всё в CDI, производство событий и подписка на них являются корректными по отношению к типам и позволяют квалификаторам определять, за какими событиями будут наблюдать наблюдатели.

Для реализации этих функций не потребуется значительных изменений при использовании приложения, которое было создано в ходе предыдущих учебных курсов. Мы как раз можем предоставить другую реализацию ItemErrorHandler (созданную в предыдущем учебном курсе), которая создает событие при каждой обработке элемента. Присваиваем этому классу имя EventItemHandler, внедряем его в ItemProcessor и используем квалификатор Notify, чтобы выбрать его для внедрения.

На диаграмме CDI отображаются объекты, созданные в этом упражнении
  1. Сначала необходимо извлечь пример начального проекта из файла cdiDemo3.zip (см. выше таблицу с перечислением требуемых ресурсов). Выберите File ("Файл") > Open Project ("Открыть проект") (Ctrl-Shift-O; ⌘-Shift-O on Mac) и выберите проект в его местоположении на компьютере.
  2. Создайте класс с именем EventItemHandler. Щелкните 'Создать файл' ( Кнопка 'Создать файл' ) или нажмите сочетание клавиш CTRL+N (⌘-N on Mac) для открытия мастера создания файлов.
  3. Выберите категорию Java, а затем выберите класс Java. Нажмите кнопку "Далее".
  4. Введите EventItemHandler в качестве имени класса, затем укажите exercise4 в качестве пакета.
  5. Нажмите кнопку "Завершить". Выполняется создание нового класса и пакета, и новый класс открывается в редакторе.
  6. Реализуйте EventItemHandler следующим образом.
    public class EventItemHandler implements ItemErrorHandler {
    
        @Inject
        private Event<Item> itemEvent;
    
        @Override
        public void handleItem(Item item) {
            System.out.println("Firing Event");
            itemEvent.fire(item);
        }
    }
    Мы внедряем экземпляр Event, где информационным наполнением события будет Item. Информационным наполнением события являются данные состояния, передаваемые от производителя событий в наблюдатель событий, который в данном случае передает отклоненный элемент. При обработке недопустимого элемента мы инициируем событие и передаем полученный недопустимый элемент. Обработчик элементов на базе событий внедряется таким же образом, как любой другой обработчик элементов, поэтому его можно загружать и выгружать по мере необходимости, а также заменять во время тестирования.
  7. Исправление всех операторов импорта. Либо щелкните правой кнопкой мыши в редакторе и выберите 'Исправить выражения импорта' или нажмите Ctrl-Shift-I (⌘-Shift-I в Mac). Убедитесь в том, что javax.enterprise.event.Event выбрано в качестве полного имени для класса Event.
    Диалоговое окно 'Исправить выражения импорта'

    Нажмите сочетание клавиш CTRL+ПРОБЕЛ на элементе Event для просмотра определения документации Javadoc для класса. Тут же определяется используемый выше метод fire().
    Всплывающее окно Javadoc в редакторе
  8. Создайте квалификатор с именем Notify. (Квалификаторы были рассмотрены в разделе Работа со внедрением и квалификаторами в CDI.)
  9. Щелкните 'Создать файл' ( Кнопка 'Создать файл' ) или нажмите сочетание клавиш CTRL+N (⌘-N on Mac) для открытия мастера создания файлов.
  10. Выберите категорию "Внедрение контекстов и зависимостей", затем выберите "Тип "квалификатора". Нажмите кнопку "Далее".
  11. Введите Notify в качестве имени класса, затем укажите exercise4 в качестве пакета.
  12. Нажмите кнопку "Завершить". Новый квалификатор Notify открывается в редакторе.
    @Qualifier
    @Retention(RUNTIME)
    @Target({METHOD, FIELD, PARAMETER, TYPE})
    public @interface Notify {
    }
  13. Добавьте аннотацию @Notify к EventItemHandler.
    @Notify
    public class EventItemHandler implements ItemErrorHandler {
    
        ...
    }
    Мы создали аннотацию квалификатора @Notify, чтобы определить обработчик ошибок для внедрения, и можем ее использовать в ItemProcessor путем добавления к точке внедрения.
  14. Добавьте аннотацию @Notify к точке внедренияEventItemHandler в exercise2.ItemProcessor.
    @Named
    @RequestScoped
    public class ItemProcessor {
    
        @Inject @Demo
        private ItemDao itemDao;
    
        @Inject
        private ItemValidator itemValidator;
    
        @Inject @Notify
        private ItemErrorHandler itemErrorHandler;
    
        public void execute() {
            List<Item> items = itemDao.fetchItems();
            for (Item item : items) {
                if (!itemValidator.isValid(item)) {
                    itemErrorHandler.handleItem(item);
                }
            }
        }
    }
    Используйте подсказку редактора, чтобы добавить оператор импорта для exercise4.Notify.
  15. Для запуска проекта нажмите кнопку 'Запустить проект' ( Кнопка 'Выполнить проект' ).
  16. В браузере нажмите кнопку Выполнить, затем вернитесь в среду IDE и проверьте протокол сервера в окне вывода (Ctrl-4; ⌘-4 в Mac). Поскольку в создаваемом приложении в настоящий момент используется DefaultItemDao для настройки четырех элементов Item, затем применяется RelaxedItemValidator в элементах Item, ожидается, что инициированиеitemErrorHandler произойдет дважды.
    Окно вывода - журнал сервера GlassFish
    Однако в настоящее время отсутствует наблюдение за событием. Это можно исправить путем создания метода наблюдателя с помощью аннотации @Observes. Это единственное, что нужно для наблюдения за событием. Для демонстрации можно изменить класс FileErrorReporter (созданный в предыдущем учебном курсе), реагирующий на инициированные события, путем добавления метода наблюдателя, который вызывает метод handleItem().
  17. Для создания ответа FileErrorReporter на событие добавьте следующий метод к классу.
    public class FileErrorReporter implements ItemErrorHandler {
    
        public void eventFired(@Observes Item item) {
            handleItem(item);
        }
    
        ...
    }
    Используйте подсказку редактора, чтобы добавить оператор импорта для javax.enterprise.event.Observes.
  18. Запустите проект еще раз (нажмите клавишу F6; fn+F6 на компьютерах Mac), нажмите кнопку Выполнить, затем вернитесь в среду IDE и проверьте протокол сервера в окне вывода.
    Окно вывода - журнал сервера GlassFish
    Вы увидите, что события инициируются для недопустимых объектов, так же как и раньше, но теперь информация об элементе сохраняется при инициировании каждого события. Также можно отметить, что выполняется наблюдение за событиями жизненного цикла, поскольку компонент FileErrorReporter создаётся и закрывается для каждого инициированного события. (Для обсуждения аннотаций жизненного цикла, например @PostConstruct и @PreDestroy ознакомьтесь с разделом Применение компонентов @Alternative и аннотаций жизненного цикла.)

Как показано выше аннотация @Observes упрощает процесс наблюдения за событиями.

События и наблюдатели также можно аннотировать с помощью квалификаторов, чтобы наблюдатели могли наблюдать только за определенными событиями для элемента. Введение в CDI и JSF 2.0


Обработка областей действия

В настоящем состоянии приложения компонент FileErrorReporter создается при каждом возникновении события. В этом случае не требуется создавать каждый раз новый компонент, поскольку отсутствует необходимость открывать и закрывать файл для каждого элемента. Однако всё ещё требуется открывать файл при запуске процесса и затем закрывать его после завершения процесса. Следовательно, необходимо учитывать область действия компонента FileErrorReporter.

В настоящее время компонент FileErrorReporter не имеет определенной области действия. Если область действия не определена, CDI использует псевдозависимую область действия по умолчанию. На практике это означает, что компонент создается и уничтожается за очень короткий промежуток времени, как правило, за время вызова метода. В нашей ситуации компонент создается и уничтожается за время инициирования события. Чтобы это исправить, можно увеличить область действия компонента вручную путем добавления аннотации области действия. Компонент @RequestScoped будет настроен таким образом, что когда он будет создан при инициировании первого события, он будет продолжать существовать на всем протяжении действия запроса. Это также означает, что для любых точек внедрения, в которых этот компонент определен для внедрения, будет внедрён тот же экземпляр компонента.

  1. Добавьте аннотацию @RequestScope и соответствующий оператор импорта для javax.enterprise.context.RequestScoped к классу FileErrorReporter.
    import javax.enterprise.context.RequestScoped;
    ...
    
    @RequestScoped
    public class FileErrorReporter implements ItemErrorHandler { ... }
    Нажмите сочетание клавиш CTRL+ПРОБЕЛ при вводе, чтобы вызвать поддержку автозавершения кода в редакторе. При выборе элемента через автозавершение кода некоторые операторы импорта автоматически добавляются к этому классу.
    Всплывающее окно завершения кода в редакторе
  2. Запустите проект еще раз (нажмите клавишу F6; fn+F6 на компьютерах Mac), нажмите кнопку Выполнить, затем вернитесь в среду IDE и проверьте протокол сервера в окне вывода.
    Окно вывода - журнал сервера GlassFish
    Обратите внимание, что компонент FileErrorReporter создается только при инициировании первого события и закрывается после инициирования конечного события.
    INFO: Firing Event
    INFO: Creating file error reporter
    INFO: Saving  [Value=34, Limit=7] to file
    INFO: Firing Event
    INFO: Saving  [Value=89, Limit=32] to file
    INFO: Closing file error reporter
    

События являются лучшим способом для разделения частей системы на модули, так как наблюдатели и производители событий не имеют информации друг о друге, и для этого их не требуется особым образом настраивать. Можно добавлять фрагменты кода, выполняющие подписку на события, при этом производитель событий не будет иметь информации о наблюдателе. (Если события не используются, то необходимо настроить производитель событий на вызов наблюдателя вручную.) Например, если кто-нибудь обновляет состояние заказа, можно добавить события для отправки письма торговому представителю или для уведомления менеджера по работе с клиентами, если вопрос, заданный в техническую поддержку, не закрыт по истечении одной недели. Такого рода правила можно внедрять без событий, но события упрощают процесс отвязывания бизнес-логики. Кроме того, отсутствует зависимость от времени компиляции или сборки. Можно просто добавить модули в приложение, и они автоматически запустят наблюдение за событиями и их производство.


Дополнительные сведения

Дополнительные сведения о CDI и Java EE приведены в следующих материалах.

Материалы по NetBeans

Внешние ресурсы

get support for the NetBeans

Support


By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2013, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo