blob: f07b20d538df0dad14409838a38676bcb1e630a2 [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.
//
= Применение аннотации @Alternative и аннотаций жизненного цикла
:jbake-type: tutorial
:jbake-tags: tutorials
:jbake-status: published
:icons: font
:syntax: true
:source-highlighter: pygments
:toc: left
:toc-title:
:description: Применение аннотации @Alternative и аннотаций жизненного цикла - Apache NetBeans
:keywords: Apache NetBeans, Tutorials, Применение аннотации @Alternative и аннотаций жизненного цикла
_Предоставлено Энди Гибсоном (Andy Gibson)_
== Внедрение контекстов и зависимостей
1. link:cdi-intro.html[+Введение в CDI и JSF 2.0+]
2. link:cdi-inject.html[+Работа с внедрением и квалификаторами в CDI+]
3. *Применение аннотации @Alternative и аннотаций жизненного цикла*
* <<alternative,Обработка нескольких развертываний>>
* <<lifecycle,Применение аннотаций жизненного цикла к управляемым компонентам>>
* <<seealso,Дополнительные сведения>>
[start=4]
. link:cdi-events.html[+Обработка событий в CDI+]
Внедрение контекстов и зависимостей (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/2009/12/22/getting-started-with-cdi-part-2-injection/[+Начало работы с CDI. Часть 2. Внедрение+]. В нем рассматривается использование аннотации `@Alternative` для настройки приложения для различных развертываний, а также показано, как использовать аннотации жизненного цикла управляемых компонентов, например, `@PostConstruct` и `@PreDestroy` для совмещения внедрения CDI с функциональными возможностями link:http://jcp.org/en/jsr/detail?id=316[+спецификации управляемых компонентов Java EE 6+]
В 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%252FcdiDemo2.zip[+cdiDemo2.zip+] |неприменимо
|===
[NOTE]
====
* В комплект Java для IDE NetBeans также входит компонент GlassFish Server Open Source Edition, являющийся контейнером, совместимым с Java EE.
* Пример проекта к этому учебному курсу можно загрузить здесь: link:https://netbeans.org/projects/samples/downloads/download/Samples%252FJavaEE%252FcdiDemo3.zip[+cdiDemo3.zip+]
====
[[alternative]]
== Обработка нескольких развертываний
CDI предлагает использовать аннотацию `@Alternative`, которая позволяет пакетировать несколько компонентов, соответствующих одной точке внедрения, без неоднозначностей. Другими словами, аннотация `@Alternative` может применяться к двум или нескольким компонентам, а затем, в зависимости от развертывания, в файле настройки CDI `beans.xml` указывается компонент для использования.
Для наглядности рассмотрим следующий случай. `ItemValidator` добавляется в главный класс `ItemProcessor`. `ItemValidator` имеет две реализации: `DefaultItemValidator` и `RelaxedItemValidator`. В соответствие с требованиями к развертыванию в большинстве случаев требуется использовать `DefaultItemValidator`, а `RelaxedItemValidator` в одном конкретном развертывании. Для этого создаются аннотации к каждому компоненту, а затем указывается компонент, который используется для данного развертывания путем добавления записей в файл приложения `beans.xml` .
image::images/cdi-diagram-alternative.png[title="Используйте внедрение CDI для свободного взаимодействия классов в используемом приложении"]
1. Начните работу с извлечения начального проекта из файла `cdiDemo2.zip` (см. выше <<requiredSoftware,таблицу необходимых ресурсов>>). Выберите File ("Файл") > Open Project ("Открыть проект") (Ctrl-Shift-O), затем выберите проект в его местоположении на компьютере.
2. Щелкните правой кнопкой мыши узел проекта в окне "Проекты" и выберите команду "Свойства".
3. Выберите категорию "Запуск" и убедитесь, что в списке "Сервер" выбран экземпляр GlassFish.
4. Создайте интерфейс `ItemValidator`.
Щелкните 'Создать файл' ( image:images/new-file-btn.png[] ) или нажмите сочетание клавиш CTRL+N (⌘-N on Mac) для открытия мастера создания файлов.
[start=5]
. Выберите категорию Java, а затем команду "Интерфейс Java". Нажмите кнопку "Далее".
[start=6]
. Введите имя класса в *ItemValidator* и пакет *exercise3*.
[start=7]
. Нажмите кнопку "Завершить". Интерфейс будет создан и открыт в редакторе.
[start=8]
. Добавьте метод `isValid()`, который принимает в качестве параметра объект `Item` и возвращает логическое значение `boolean`.
[source,java]
----
public interface ItemValidator {
*boolean isValid(Item item);*
}
----
С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.
[start=9]
. Расширьте класс `ItemProcessor` для добавления новой возможности. Откройте в редакторе `ItemProcessor` и внесите следующие изменения.
[source,java]
----
@Named
@RequestScoped
public class ItemProcessor {
@Inject @Demo
private ItemDao itemDao;
*@Inject
private ItemValidator itemValidator;*
public void execute() {
List<Item> items = itemDao.fetchItems();
for (Item item : items) {
System.out.println(*"Item = " + item + " valid = " + itemValidator.isValid(item)*);
}
}
}
----
С помощью подсказки редактора добавьте оператор импорта для `exercise3.ItemValidator`.
[start=10]
. Создайте реализацию `ItemValidator` под названием `DefaultItemValidator`, которая просто сравнивает значение с предельным.
В окне "Проекты" щелкните правой кнопкой пакет `exercise3` и выберите команду "Создать" > "Класс Java". Дайте классу имя *DefaultItemValidator* и нажмите кнопку "Готово".
[start=11]
. В элементе `DefaultItemValidator` реализуйте `ItemValidator` и переопределите метод `isValid()` следующим образом.
[source,java]
----
public class DefaultItemValidator *implements ItemValidator* {
*@Override
public boolean isValid(Item item) {
return item.getValue() < item.getLimit();
}*
}
----
С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.
[start=12]
. Нажмите кнопку 'Запустить проект' (image:images/run-project-btn.png[]) на главной панели инструментов IDE. Файл скомпилирован и развернут в GlassFish, и страница приветствия приложения (`process.xhtml`) отображается в веб-браузере.
[start=13]
. Нажмите кнопку `Выполнить` на странице. Вернитесь в среду IDE и проверьте протокол сервера GlassFish. Журнал сервера отображается в окне вывода (Ctrl-4; ⌘-4 в Mac) на вкладке 'GlassFish'. В нем видно, что элементы проверяются и перечисляются только допустимые элементы, значение которых меньше предельного.
[source,java]
----
INFO: Item = exercise2.Item@e857ac [Value=34, Limit=7] valid = false
INFO: Item = exercise2.Item@63124f52 [Value=4, Limit=37] valid = true
INFO: Item = exercise2.Item@4715c34e [Value=24, Limit=19] valid = false
INFO: Item = exercise2.Item@65c95a57 [Value=89, Limit=32] valid = false
----
image::images/output-window.png[title="Просмотрите журнал сервера в окне вывода"]
[start=14]
. Теперь рассмотрим случай, в котором вам необходимо выполнить развертывание в другом месте, менее жестком, считающим компонент недопустимым только в том случае, если его значение более чем в два раза превышает ограничение. Может потребоваться другой компонент для реализации интерфейса `ItemValidator` для данной логики.
Создайте новую реализацию `ItemValidator` с именем `RelaxedItemValidator`. В окне "Проекты" щелкните правой кнопкой пакет `exercise3` и выберите команду "Создать" > "Класс Java". Дайте классу имя *RelaxedItemValidator* и нажмите кнопку "Готово".
[start=15]
. Сделайте `RelaxedItemValidator` реализацией `ItemValidator` и переопределите метод `isValid()` следующим образом.
[source,java]
----
public class RelaxedItemValidator *implements ItemValidator* {
*@Override
public boolean isValid(Item item) {
return item.getValue() < (item.getLimit() * 2);
}*
}
----
С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.
[start=16]
. Для запуска проекта нажмите кнопку 'Запустить проект' ( image:images/run-project-btn.png[] ). Обратите внимание, что теперь развертывание проекта завершается сбоем.
[start=17]
. Проверьте журнал сервера в окне вывода (Ctrl-4; ⌘-4 в Mac). В протоколе отображается сообщение об ошибке неоднозначной зависимости. Это происходит по причине того, что имеются два класса, реализующих один и тот же интерфейс.
[source,java]
----
org.glassfish.deployment.common.DeploymentException: Injection point has ambiguous dependencies.
Injection point: field exercise2.ItemProcessor.itemValidator;
Qualifiers: [@javax.enterprise.inject.Default()];
Possible dependencies: [exercise3.RelaxedItemValidator, exercise3.DefaultItemValidator]
----
Реализация Weld CDI не способна определить элемент, используемый для данной точки внедрения (`RelaxedItemValidator` или `DefaultItemValidator`).
Как указано выше, единственное отличие связано с развертыванием. Для большинства развертываний можно использовать средство проверки по умолчанию, однако для одного развертывания может потребоваться использование "нежесткой" реализации. В CDI существует аннотация `@Alternative`, которая позволяет пакетировать несколько компонентов, соответствующих одной точке внедрения, без проблем неоднозначности, поскольку при этом используется компонент, указанный в файле `beans.xml` . Это позволяет развертывать в одном модуле обе реализации. При этом отличается только определение в файле `beans.xml` , которое уникально для каждой реализации.
[start=18]
. Добавьте аннотацию `@Alternative` и соответствующий оператор импорта в `RelaxedItemValidator` и `DefaultItemValidator`.
Откройте в редакторе `RelaxedItemValidator` и внесите следующие изменения.
[source,java]
----
*import javax.enterprise.inject.Alternative;*
...
*@Alternative*
public class RelaxedItemValidator implements ItemValidator {
public boolean isValid(Item item) {
return item.getValue() < (item.getLimit() * 2);
}
}
----
Введите '`@Al`', затем нажмите CTRL+ПРОБЕЛ для вызова автозавершения кода. Поскольку возможен только один вариант, аннотация `@Alternative` завершается, а в начале файла автоматически добавляется соответствующий оператор импорта для `javax.enterprise.inject.Alternative`. Как правило, при нажатии CTRL+ПРОБЕЛ в аннотациях также вызывается всплывающая документация Javadoc.
image::images/code-completion-alternative.png[title="Нажмите Ctrl-Space в аннотациях для вызова документации Javadoc"]
Переключитесь к `DefaultItemValidator` (нажмите сочетание клавиш CTRL+TAB) и внесите следующее изменение.
[source,java]
----
*import javax.enterprise.inject.Alternative;*
...
*@Alternative*
public class DefaultItemValidator implements ItemValidator {
public boolean isValid(Item item) {
return item.getValue() < item.getLimit();
}
}
----
Выполняя развертывание приложения сейчас, вы получите ошибку "неудовлетворенная зависимость", так как два подходящих компонента были определены как альтернативные, но ни один из них не был активирован в файле `beans.xml` .
[start=19]
. С помощью диалогового окна "Переход к файлу" в среде IDE откройте файл `beans.xml` . Выберите пункт "Переход" > "Переход к файлу" в главном меню среды IDE (сочетание клавиш ALT+SHIFT+O; CTRL+SHIFT+O в Mac OS), затем введите `beans`. Нажмите кнопку "ОК".
image::images/go-to-file.png[title="С помощью диалогового окна &quot;Переход к файлу&quot; быстро найдите файл проекта."]
[start=20]
. Внесите следующие изменения в файл `beans.xml`
[source,xml]
----
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
*<alternatives>
<class>exercise3.RelaxedItemValidator</class>
</alternatives>*
</beans>
----
Данная команда указывает CDI использовать `RelaxedItemValidator` для данного развертывания. Аннотация `@Alternative` может рассматриваться как способ отключения компонента, запрещения его внедрения и разрешения пакетирования реализации с другими компонентами. Добавление компонента в качестве альтернативы в файл `beans.xml` фактически снова разрешает компонент, делая его доступным для внедрения. Перемещение этого вида метаданных в файл `beans.xml` позволяет связать различные версии файла с различными развертываниями.
[start=21]
. Для запуска проекта нажмите кнопку 'Запустить проект' ( image:images/run-project-btn.png[] ). качестве альтернативы нажмите F6; fn-F6 в Mac) В браузере нажмите кнопку '`Выполнить`' на отображаемой странице. Переключите обратно в IDE и проверьте журнал сервера GlassFish, который отображается в окне вывода (Ctrl-4; ⌘-4 в Mac).
[source,java]
----
INFO: Item = exercise2.Item@672f0924 [Value=34, Limit=7] valid = false
INFO: Item = exercise2.Item@41014f68 [Value=4, Limit=37] valid = true
INFO: Item = exercise2.Item@3d04562f [Value=24, Limit=19] valid = true
INFO: Item = exercise2.Item@67b646f4 [Value=89, Limit=32] valid = false
----
Используется реализация `RelaxedItemValidator`, а третий элемент отображается как верный, хотя значение (`24`) больше заданного предела (`19`).
[[lifecycle]]
== Применение аннотаций жизненного цикла к управляемым компонентам
В этом упражнении `ItemErrorHandler` будет добавлен в главный класс `ItemProcessor`. Для добавления выбран `FileErrorReporter`, поскольку он является единственной реализацией интерфейса `ItemErrorHandler`. Для настройки зависящих от жизненного цикла действий класса следует использовать аннотации `@PostConstruct` и `@PreDestroy` из спецификации управляемых компонентов (включенной в link:http://jcp.org/en/jsr/detail?id=316[+JSR 316: спецификация платформы Java, Enterprise Edition 6+]).
image::images/cdi-diagram-lifecycle.png[title="Используйте внедрение CDI для свободного взаимодействия классов в используемом приложении"]
После этого необходимо создать интерфейс `ItemErrorHandler` для обработки обнаруженных недопустимых элементов.
1. В окне "Проекты" щелкните правой кнопкой пакет `exercise3` и выберите команду "Создать" > "Интерфейс Java".
2. В мастере интерфейсов Java введите имя класса *ItemErrorHandler* и имя пакета *exercise3*. Нажмите кнопку "Завершить".
Интерфейс будет создан и открыт в редакторе.
[start=3]
. Добавьте метод `handleItem()`, принимающий параметр типа `Item`.
[source,java]
----
public interface ItemErrorHandler {
*void handleItem(Item item);*
}
----
С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.
[start=4]
. Выполните реализацию `ItemErrorHandler` с фиктивным обработчиком `FileErrorReporter`, сохраняющим данные элемента в файл.
В окне "Проекты" щелкните правой кнопкой пакет `exercise3` и выберите команду "Создать" > "Класс Java". Присвойте классу имя *FileErrorReporter* и нажмите кнопку "Готово".
[start=5]
. Сделайте `FileErrorReporter` реализацией `ItemErrorHandler` и переопределите метод `handleItem()` следующим образом.
[source,java]
----
public class FileErrorReporter *implements ItemErrorHandler* {
*@Override
public void handleItem(Item item) {
System.out.println("Saving " + item + " to file");
}*
}
----
С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.
Вам нужно открыть файл до начала обработки элементов, оставить его открытым в течение процесса добавления содержимого в данный файл, а затем закрыть его по завершении процесса. Можно вручную добавить методы `initProcess()` и `finishProcess()` к компоненту средства сообщения об ошибке, но в этом случае вы не сможете выполнить кодирование интерфейса, так как вызывающей стороне будет необходимо знать данные специфичные для класса методы. Можно добавить те же методы к интерфейсу `ItemErrorReporter`, но в этом случае потребуется выполнить ненужное внедрение данных методов в каждый класс, реализующий данный интерфейс. Вместо этого можно использовать несколько аннотаций жизненного цикла из спецификации управляемых компонентов (входящей в link:http://jcp.org/en/jsr/detail?id=316[+JSR 316: спецификация платформы Java, Enterprise Edition 6+]) для вызова методов в компоненте в конкретных точках жизненного цикла компонента. Метод с аннотацией `@PostConstruct` вызывается после создания компонента и учета всех его зависимостей. Метод с аннотацией `@PreDestroy` аналогичным образом вызывается непосредственно перед удалением компонента контейнером.
[start=6]
. Добавьте следующие методы `init()` и `release()` с аннотациями `@PostConstruct` и `@PreDestroy`.
[source,java]
----
public class FileErrorReporter implements ItemErrorHandler {
*@PostConstruct
public void init() {
System.out.println("Creating file error reporter");
}
@PreDestroy
public void release() {
System.out.println("Closing file error reporter");
}*
@Override
public void handleItem(Item item) {
System.out.println("Saving " + item + " to file");
}
}
----
[start=7]
. Исправьте операторы импорта. Либо щелкните правой кнопкой мыши в редакторе и выберите 'Исправить выражения импорта' или нажмите Ctrl-Shift-I (⌘-Shift-I в Mac). В начало файла добавляются операторы импорта для `javax.annotation.PostConstruct` и `javax.annotation.PreDestroy`.
[start=8]
. После этого добавьте новый компонент `ItemErrorHandler` к `ItemProcessor`.
[source,java]
----
@Named
@RequestScoped
public class ItemProcessor {
@Inject @Demo
private ItemDao itemDao;
@Inject
private ItemValidator itemValidator;
*@Inject
private ItemErrorHandler itemErrorHandler;*
public void execute() {
List<Item> items = itemDao.fetchItems();
for (Item item : items) {
*if (!itemValidator.isValid(item)) {
itemErrorHandler.handleItem(item);
}*
}
}
}
----
С помощью подсказки редактора добавьте оператор импорта для `exercise3.ItemErrorHandler`.
[start=9]
. Для запуска проекта нажмите кнопку 'Запустить проект' ( image:images/run-project-btn.png[] ). качестве альтернативы нажмите F6; fn-F6 в Mac) В браузере нажмите кнопку '`Выполнить`' на отображаемой странице. Переключите обратно в IDE и проверьте журнал сервера GlassFish, который отображается в окне вывода (Ctrl-4; ⌘-4 в Mac).
[source,java]
----
INFO: Creating file error reporter
INFO: Saving exercise2.Item@6257d812 [Value=34, Limit=7] to file
INFO: Saving exercise2.Item@752ab82e [Value=89, Limit=32] to file
INFO: Closing file error reporter
----
link:/about/contact_form.html?to=3&subject=Feedback:%20Using%20CDI%20Injection%20to%20Perform%20Custom%20Validation[+Отправить отзыв по этому учебному курсу+]
[[seealso]]
== Дополнительные сведения
Различные развертывания приложений могут использовать различные правила обработки недопустимых элементов: отклонение элементов, отправку уведомлений, выделение элементов или перечисление их в выходном файле. Кроме того, может потребоваться комбинация этих действий (например, отклонить заказ, отправить письмо менеджеру и записать заказ в файл). Оптимальным способом обработки такой многогранной проблемы является использование _событий_. События CDI рассматриваются в последнем примере этой серии.
* link:cdi-events.html[+Обработка событий в CDI+]
Дополнительные сведения о CDI и Java EE приведены в следующих материалах.
* link:cdi-intro.html[+Начало работы со внедрением контекстов и зависимостей и JSF 2.0+]
* link:cdi-inject.html[+Работа с внедрением и квалификаторами в CDI+]
* link:javaee-gettingstarted.html[+Начало работы с приложениями Java EE+]
* 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+]