blob: eb89837d4acd34586d5238478f89e9a667418953 [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
= Работа с событиями в CDI
:jbake-type: tutorial
:jbake-tags: tutorials
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: Работа с событиями в CDI - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, Работа с событиями в CDI
_Предоставлено Энди Гибсоном (Andy Gibson)_
== Внедрение контекстов и зависимостей
1. link:cdi-intro.html[+Введение в CDI и JSF 2.0+]
2. link:cdi-inject.html[+Работа с внедрением и квалификаторами в CDI+]
3. link:cdi-validate.html[+Применение аннотации @Alternative и аннотаций жизненного цикла+]
4. *Обработка событий в CDI*
* <<event,Использование событий>>
* <<scopes,Обработка областей действий>>
* <<seealso,Дополнительные сведения>>
Внедрение контекстов и зависимостей (CDI), определяемое документом link:http://jcp.org/en/jsr/detail?id=299[+JSR-299+], является неотъемлемой частью Java EE 6 и обеспечивает архитектуру, позволяющую компонентам Java EE (например, сервлетам, компонентам EJB и JavaBeans) существовать в жизненном цикле приложения с четко определенными контекстами. Кроме того, службы CDI позволяют компонентам Java EE (например, компонентам сеансов EJB и управляемым компонентам JavaServer Faces) внедряться и свободно взаимодействовать путем запуска и обработки событий.
Этот учебный курс основан на записи блога Энди Гибсона (Andy Gibson) link:http://www.andygibson.net/blog/index.php/2010/01/11/getting-started-with-jsf-2-0-and-cdi-part-3/[+Начало работы с CDI. Часть 3. События+]. Здесь рассмотрены способы использования концепции _событий_ в Java EE, с помощью которой производят возникающие в приложении события и подписываются на них (т.е. _наблюдают_) таким образом, что это позволяет управлять несвязанным кодом между производителями и наблюдателями. Класс `javax.enterprise.event.Event` применяют для создания событий, а аннотацию `@Observes` CDI — для подписки на события.
В NetBeans IDE обеспечена встроенная поддержка для внедрения контекстов и зависимостей, включая поддержку создания файла конфигурации CDI `beans.xml` при создании проекта, поддержку редактора и навигации для аннотаций, а также различных мастеров для создания часто используемых артефактов CD.
Для работы с этим учебным курсом требуется программное обеспечение и материалы, перечисленные ниже.
|===
|Программное обеспечение или материал |Требуемая версия
|link:https://netbeans.org/downloads/index.html[+IDE NetBeans+] |Версия 7.2, 7.3, 7.4, 8.0, Java EE
|link:http://www.oracle.com/technetwork/java/javase/downloads/index.html[+Комплект для разработчика на языке Java (JDK)+] |версия 7 или 8
|link:http://glassfish.dev.java.net/[+Сервер GlassFish+] |Open Source Edition 3.x или 4.x
|link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252FcdiDemo3.zip[+cdiDemo3.zip+] |неприменимо
|===
[NOTE]
====
* В комплект Java EE для IDE NetBeans также входит компонент GlassFish Server Open Source Edition 3.x, являющийся контейнером, совместимым с Java EE.
* Демонстрационный проект решения для этого учебного курса можно загрузить здесь: link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252FcdiDemoComplete.zip[+cdiDemoComplete.zip+]
====
[[event]]
== Использование событий
В предыдущем учебном курсе link:cdi-validate.html[+Применение компонентов @Alternative и аннотаций жизненного цикла+] у нас было приложение, в котором был получен список элементов, они проверялись, и было выполнено соответствующее действие при обнаружении недопустимого элемента. Предположим, в будущем нам потребуется расширить систему для обработки всевозможных ситуаций, которые происходят при обнаружении недопустимого элемента. Такими ситуациями могут быть отправка электронной почты, изменения других данных, например, отмена заказа, сохранение списка отклонений в файле или таблице базы данных. Для полного развязывания реализации можно использовать _события_ в Java EE. События создаются _производителем_, а подписка на них выполняется _наблюдателем_ событий. Как и практически всё в CDI, производство событий и подписка на них являются корректными по отношению к типам и позволяют квалификаторам определять, за какими событиями будут наблюдать наблюдатели.
Для реализации этих функций не потребуется значительных изменений при использовании приложения, которое было создано в ходе предыдущих учебных курсов. Мы как раз можем предоставить другую реализацию `ItemErrorHandler` (созданную в link:cdi-validate.html[+предыдущем учебном курсе+]), которая создает событие при каждой обработке элемента. Присваиваем этому классу имя `EventItemHandler`, внедряем его в `ItemProcessor` и используем квалификатор `Notify`, чтобы выбрать его для внедрения.
image::images/cdi-diagram-events.png[title="Используйте внедрение CDI для свободного взаимодействия классов в используемом приложении"]
1. Сначала необходимо извлечь пример начального проекта из файла `cdiDemo3.zip` (см. выше <<requiredSoftware,таблицу с перечислением требуемых ресурсов>>). Выберите File ("Файл") > Open Project ("Открыть проект") (Ctrl-Shift-O; ⌘-Shift-O on Mac) и выберите проект в его местоположении на компьютере.
2. Создайте класс с именем `EventItemHandler`. Щелкните 'Создать файл' ( image:images/new-file-btn.png[] ) или нажмите сочетание клавиш CTRL+N (⌘-N on Mac) для открытия мастера создания файлов.
3. Выберите категорию Java, а затем выберите класс Java. Нажмите кнопку "Далее".
4. Введите *EventItemHandler* в качестве имени класса, затем укажите *exercise4* в качестве пакета.
5. Нажмите кнопку "Завершить". Выполняется создание нового класса и пакета, и новый класс открывается в редакторе.
6. Реализуйте *EventItemHandler* следующим образом.
[source,java]
----
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`. Информационным наполнением события являются данные состояния, передаваемые от производителя событий в наблюдатель событий, который в данном случае передает отклоненный элемент. При обработке недопустимого элемента мы инициируем событие и передаем полученный недопустимый элемент. Обработчик элементов на базе событий внедряется таким же образом, как любой другой обработчик элементов, поэтому его можно загружать и выгружать по мере необходимости, а также заменять во время тестирования.
. Исправление всех операторов импорта. Либо щелкните правой кнопкой мыши в редакторе и выберите 'Исправить выражения импорта' или нажмите Ctrl-Shift-I (⌘-Shift-I в Mac). Убедитесь в том, что `javax.enterprise.event.Event` выбрано в качестве полного имени для класса `Event`.
image::images/fix-all-imports.png[title="Щелкните в редакторе правой кнопкой мыши и выберите &quot;Исправить операторы импорта&quot;. "]
[tips]#Нажмите сочетание клавиш CTRL+ПРОБЕЛ на элементе `Event` для просмотра определения документации Javadoc для класса. Тут же определяется используемый выше метод `fire()`.#
image::images/event-javadoc.png[title="Нажмите Ctrl-Space для просмотра документации Javadoc по классам в API"]
. Создайте квалификатор с именем `Notify`. (Квалификаторы были рассмотрены в разделе link:cdi-inject.html[+Работа со внедрением и квалификаторами в CDI+].)
. Щелкните 'Создать файл' ( image:images/new-file-btn.png[] ) или нажмите сочетание клавиш CTRL+N (⌘-N on Mac) для открытия мастера создания файлов.
. Выберите категорию "Внедрение контекстов и зависимостей", затем выберите "Тип "квалификатора". Нажмите кнопку "Далее".
. Введите *Notify* в качестве имени класса, затем укажите *exercise4* в качестве пакета.
. Нажмите кнопку "Завершить". Новый квалификатор `Notify` открывается в редакторе.
[source,java]
----
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Notify {
}
----
. Добавьте аннотацию `@Notify` к `EventItemHandler`.
[source,java]
----
*@Notify*
public class EventItemHandler implements ItemErrorHandler {
...
}
----
Мы создали аннотацию квалификатора `@Notify`, чтобы определить обработчик ошибок для внедрения, и можем ее использовать в `ItemProcessor` путем добавления к точке внедрения.
. Добавьте аннотацию `@Notify` к точке внедрения`EventItemHandler` в `exercise2.ItemProcessor`.
[source,java]
----
@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`.
. Для запуска проекта нажмите кнопку 'Запустить проект' ( image:images/run-project-btn.png[] ).
. В браузере нажмите кнопку `Выполнить`, затем вернитесь в среду IDE и проверьте протокол сервера в окне вывода (Ctrl-4; ⌘-4 в Mac). Поскольку в создаваемом приложении в настоящий момент используется `DefaultItemDao` для настройки четырех элементов `Item`, затем применяется `RelaxedItemValidator` в элементах `Item`, ожидается, что инициирование`itemErrorHandler` произойдет дважды.
image::images/output-window.png[title="Просмотрите журнал сервера GlassFish, отображенный в окне вывода"]
Однако в настоящее время отсутствует наблюдение за событием. Это можно исправить путем создания метода _наблюдателя_ с помощью аннотации `@Observes`. Это единственное, что нужно для наблюдения за событием. Для демонстрации можно изменить класс `FileErrorReporter` (созданный в link:cdi-validate.html[+предыдущем учебном курсе+]), реагирующий на инициированные события, путем добавления метода наблюдателя, который вызывает метод `handleItem()`.
. Для создания ответа `FileErrorReporter` на событие добавьте следующий метод к классу.
[source,java]
----
public class FileErrorReporter implements ItemErrorHandler {
*public void eventFired(@Observes Item item) {
handleItem(item);
}*
...
}
----
Используйте подсказку редактора, чтобы добавить оператор импорта для `javax.enterprise.event.Observes`.
. Запустите проект еще раз (нажмите клавишу F6; fn+F6 на компьютерах Mac), нажмите кнопку `Выполнить`, затем вернитесь в среду IDE и проверьте протокол сервера в окне вывода.
image::images/output-window2.png[title="Просмотрите журнал сервера GlassFish, отображенный в окне вывода"]
Вы увидите, что события инициируются для недопустимых объектов, так же как и раньше, но теперь информация об элементе сохраняется при инициировании каждого события. Также можно отметить, что выполняется наблюдение за событиями жизненного цикла, поскольку компонент `FileErrorReporter` создаётся и закрывается для каждого инициированного события. (Для обсуждения аннотаций жизненного цикла, например `@PostConstruct` и `@PreDestroy` ознакомьтесь с разделом link:cdi-validate.html[+Применение компонентов @Alternative и аннотаций жизненного цикла+].)
Как показано выше аннотация `@Observes` упрощает процесс наблюдения за событиями.
События и наблюдатели также можно аннотировать с помощью квалификаторов, чтобы наблюдатели могли наблюдать только за определенными событиями для элемента. link:http://www.andygibson.net/blog/index.php/2010/01/11/getting-started-with-jsf-2-0-and-cdi-part-3/[+Введение в CDI и JSF 2.0+]
[[scopes]]
== Обработка областей действия
В настоящем состоянии приложения компонент `FileErrorReporter` создается при каждом возникновении события. В этом случае не требуется создавать каждый раз новый компонент, поскольку отсутствует необходимость открывать и закрывать файл для каждого элемента. Однако всё ещё требуется открывать файл при запуске процесса и затем закрывать его после завершения процесса. Следовательно, необходимо учитывать _область действия_ компонента `FileErrorReporter`.
В настоящее время компонент `FileErrorReporter` не имеет определенной области действия. Если область действия не определена, CDI использует псевдозависимую область действия по умолчанию. На практике это означает, что компонент создается и уничтожается за очень короткий промежуток времени, как правило, за время вызова метода. В нашей ситуации компонент создается и уничтожается за время инициирования события. Чтобы это исправить, можно увеличить область действия компонента вручную путем добавления аннотации области действия. Компонент `@RequestScoped` будет настроен таким образом, что когда он будет создан при инициировании первого события, он будет продолжать существовать на всем протяжении действия запроса. Это также означает, что для любых точек внедрения, в которых этот компонент определен для внедрения, будет внедрён тот же экземпляр компонента.
1. Добавьте аннотацию `@RequestScope` и соответствующий оператор импорта для `javax.enterprise.context.RequestScoped` к классу `FileErrorReporter`.
[source,java]
----
*import javax.enterprise.context.RequestScoped;*
...
*@RequestScoped*
public class FileErrorReporter implements ItemErrorHandler { ... }
----
TIP: Нажмите сочетание клавиш CTRL+ПРОБЕЛ при вводе, чтобы вызвать поддержку автозавершения кода в редакторе. При выборе элемента через автозавершение кода некоторые операторы импорта автоматически добавляются к этому классу.#
image::images/code-completion.png[title="Нажмите сочетание клавиш CTRL+ПРОБЕЛ при вводе, чтобы вызвать поддержку автозавершения кода в редакторе. "]
. Запустите проект еще раз (нажмите клавишу F6; fn+F6 на компьютерах Mac), нажмите кнопку `Выполнить`, затем вернитесь в среду IDE и проверьте протокол сервера в окне вывода.
image::images/output-window3.png[title="Просмотрите журнал сервера GlassFish, отображенный в окне вывода"]
Обратите внимание, что компонент `FileErrorReporter` создается только при инициировании первого события и закрывается после инициирования конечного события.
[source,java]
----
INFO: Firing Event
*INFO: Creating file error reporter*
INFO: Saving exercise2.Item@48ce88f6 [Value=34, Limit=7] to file
INFO: Firing Event
INFO: Saving exercise2.Item@3cae5788 [Value=89, Limit=32] to file
*INFO: Closing file error reporter*
----
События являются лучшим способом для разделения частей системы на модули, так как наблюдатели и производители событий не имеют информации друг о друге, и для этого их не требуется особым образом настраивать. Можно добавлять фрагменты кода, выполняющие подписку на события, при этом производитель событий не будет иметь информации о наблюдателе. (Если события не используются, то необходимо настроить производитель событий на вызов наблюдателя вручную.) Например, если кто-нибудь обновляет состояние заказа, можно добавить события для отправки письма торговому представителю или для уведомления менеджера по работе с клиентами, если вопрос, заданный в техническую поддержку, не закрыт по истечении одной недели. Такого рода правила можно внедрять без событий, но события упрощают процесс отвязывания бизнес-логики. Кроме того, отсутствует зависимость от времени компиляции или сборки. Можно просто добавить модули в приложение, и они автоматически запустят наблюдение за событиями и их производство.
link:/about/contact_form.html?to=3&subject=Feedback:%20Working%20with%20Events%20in%20CDI[+Отправить отзыв по этому учебному курсу+]
[[seealso]]
== Дополнительные сведения
Дополнительные сведения о CDI и Java EE приведены в следующих материалах.
=== Материалы по NetBeans
* link:cdi-intro.html[+Начало работы со внедрением контекстов и зависимостей и JSF 2.0+]
* link:cdi-inject.html[+Работа с внедрением и квалификаторами в CDI+]
* link:cdi-validate.html[+Применение аннотации @Alternative и аннотаций жизненного цикла+]
* link:javaee-gettingstarted.html[+Начало работы с приложениями Java EE+]
* link:../web/jsf20-intro.html[+Введение в JavaServer Faces 2.0+]
=== Внешние ресурсы
* link:http://blogs.oracle.com/enterprisetechtips/entry/using_cdi_and_dependency_injection[+Технические рекомендации по Java EE: использование CDI и внедрения зависимостей для Java в приложении JSF 2.0+]
* link:http://download.oracle.com/javaee/6/tutorial/doc/gjbnr.html[+Учебный курс по Java EE 6, часть V: внедрение контекстов и зависимостей для платформы Java EE+]
* link:http://jcp.org/en/jsr/detail?id=299[+JSR 299: спецификация внедрения контекстов и зависимостей+]
* link:http://jcp.org/en/jsr/detail?id=316[+JSR 316. Платформа Java, спецификация Enterprise Edition 6+]