// 
//     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,Дополнительные сведения>>


. 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) для открытия мастера создания файлов.


. Выберите категорию Java, а затем команду "Интерфейс Java". Нажмите кнопку "Далее".


. Введите имя класса в *ItemValidator* и пакет *exercise3*.


. Нажмите кнопку "Завершить". Интерфейс будет создан и открыт в редакторе.


. Добавьте метод `isValid()`, который принимает в качестве параметра объект `Item` и возвращает логическое значение `boolean`.

[source,java]
----

public interface ItemValidator {
    *boolean isValid(Item item);*
}
----
С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.


. Расширьте класс `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`.



. Создайте реализацию `ItemValidator` под названием `DefaultItemValidator`, которая просто сравнивает значение с предельным.

В окне "Проекты" щелкните правой кнопкой пакет `exercise3` и выберите команду "Создать" > "Класс Java". Дайте классу имя *DefaultItemValidator* и нажмите кнопку "Готово".



. В элементе `DefaultItemValidator` реализуйте `ItemValidator` и переопределите метод `isValid()` следующим образом.

[source,java]
----

public class DefaultItemValidator *implements ItemValidator* {

    *@Override
    public boolean isValid(Item item) {
        return item.getValue() < item.getLimit();
    }*
}
----

С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.



. Нажмите кнопку 'Запустить проект' (image:images/run-project-btn.png[]) на главной панели инструментов IDE. Файл скомпилирован и развернут в GlassFish, и страница приветствия приложения (`process.xhtml`) отображается в веб-браузере.


. Нажмите кнопку `Выполнить` на странице. Вернитесь в среду 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="Просмотрите журнал сервера в окне вывода"]



. Теперь рассмотрим случай, в котором вам необходимо выполнить развертывание в другом месте, менее жестком, считающим компонент недопустимым только в том случае, если его значение более чем в два раза превышает ограничение. Может потребоваться другой компонент для реализации интерфейса `ItemValidator` для данной логики.

Создайте новую реализацию `ItemValidator` с именем `RelaxedItemValidator`. В окне "Проекты" щелкните правой кнопкой пакет `exercise3` и выберите команду "Создать" > "Класс Java". Дайте классу имя *RelaxedItemValidator* и нажмите кнопку "Готово".



. Сделайте `RelaxedItemValidator` реализацией `ItemValidator` и переопределите метод `isValid()` следующим образом.

[source,java]
----

public class RelaxedItemValidator *implements ItemValidator* {

    *@Override
    public boolean isValid(Item item) {
        return item.getValue() < (item.getLimit() * 2);
    }*
}
----

С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.



. Для запуска проекта нажмите кнопку 'Запустить проект' ( image:images/run-project-btn.png[] ). Обратите внимание, что теперь развертывание проекта завершается сбоем.


. Проверьте журнал сервера в окне вывода (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` , которое уникально для каждой реализации.



. Добавьте аннотацию `@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` .



. С помощью диалогового окна "Переход к файлу" в среде IDE откройте файл `beans.xml` . Выберите пункт "Переход" > "Переход к файлу" в главном меню среды IDE (сочетание клавиш ALT+SHIFT+O; CTRL+SHIFT+O в Mac OS), затем введите `beans`. Нажмите кнопку "ОК". 

image::images/go-to-file.png[title="С помощью диалогового окна &quot;Переход к файлу&quot; быстро найдите файл проекта."]



. Внесите следующие изменения в файл `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` позволяет связать различные версии файла с различными развертываниями.



. Для запуска проекта нажмите кнопку 'Запустить проект' ( 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*. Нажмите кнопку "Завершить".

Интерфейс будет создан и открыт в редакторе.



. Добавьте метод `handleItem()`, принимающий параметр типа `Item`.

[source,java]
----

public interface ItemErrorHandler {
    *void handleItem(Item item);*
}
----

С помощью подсказки редактора добавьте оператор импорта для `exercise2.Item`.



. Выполните реализацию `ItemErrorHandler` с фиктивным обработчиком `FileErrorReporter`, сохраняющим данные элемента в файл.

В окне "Проекты" щелкните правой кнопкой пакет `exercise3` и выберите команду "Создать" > "Класс Java". Присвойте классу имя *FileErrorReporter* и нажмите кнопку "Готово".



. Сделайте `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` аналогичным образом вызывается непосредственно перед удалением компонента контейнером.



. Добавьте следующие методы `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");
    }
}
----


. Исправьте операторы импорта. Либо щелкните правой кнопкой мыши в редакторе и выберите 'Исправить выражения импорта' или нажмите Ctrl-Shift-I (⌘-Shift-I в Mac). В начало файла добавляются операторы импорта для `javax.annotation.PostConstruct` и `javax.annotation.PreDestroy`.


. После этого добавьте новый компонент `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`.



. Для запуска проекта нажмите кнопку 'Запустить проект' ( 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+]
