corner imagecorner image
IDEPlatformPluginsDocs & SupportCommunityPartners

Применение аннотации @Alternative и аннотаций жизненного цикла

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

Содержание данной страницы применимо к среде IDE NetBeans 6.9, 7.0 и 7.1

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

Этот учебный курс основан на записи блога Энди Гибсона (Andy Gibson) под заголовком Введение в CDI, часть 2: Внедрение В нем рассматривается использование аннотации @Alternative для настройки приложения для различных развертываний, а также показано, как использовать аннотации жизненного цикла управляемых компонентов, например, @PostConstruct и @PreDestroy для совмещения внедрения CDI с функциональными возможностями спецификации управляемых компонентов Java EE 6

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


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

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

Примечания.

  • В состав пакета Java среды IDE NetBeans также входит сервер GlassFish Open Source Edition 3.x, являющийся контейнером, совместимым с Java EE 6.
  • Поддержка CDI также доступна в NetBeans 6.8 после установки Исправления 1.
  • Пример проекта к этому учебному курсу можно загрузить здесь: cdiDemo3.zip

Обработка нескольких развертываний

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

Для наглядности рассмотрим следующий случай. ItemValidator добавляется в главный класс ItemProcessor. ItemValidator имеет две реализации: DefaultItemValidator и RelaxedItemValidator. В соответствие с требованиями к развертыванию в большинстве случаев требуется использовать DefaultItemValidator, а RelaxedItemValidator — в одном конкретном развертывании. Для этого создаются аннотации к каждому компоненту, а затем указывается компонент, который используется для данного развертывания путем добавления записей в файл приложения beans.xml.

Схема CDI создаваемых в этом упражнении объектов
  1. Начните работу с извлечения начального проекта из файла cdiDemo2.zip (см. выше таблицу необходимых ресурсов). Откройте проект в среде IDE, выбрав "Файл" > "Открыть проект" (Ctrl-Shift-O; ⌘-Shift-O на Mac), затем выбрав проект из его местоположения на компьютере.
  2. Создайте интерфейс ItemValidator.

    Щелкните "Создать файл"кнопка "Создать файл" или нажмите Ctrl-N (⌘-N на Mac), чтобы открыть мастер создания файла.
  3. Выберите категорию Java, а затем команду "Интерфейс Java". Нажмите кнопку "Далее".
  4. Введите имя класса в ItemValidator и пакет exercise3.
  5. Нажмите кнопку "Готово". Интерфейс будет создан и открыт в редакторе.
  6. Добавьте метод isValid(), который принимает в качестве параметра объект Item и возвращает логическое значение boolean.
    public interface ItemValidator {
        boolean isValid(Item item);
    }
    С помощью подсказки редактора добавьте оператор импорта для exercise2.Item.
  7. Расширьте класс ItemProcessor для добавления новой возможности. Откройте в редакторе ItemProcessor и внесите следующие изменения.
    @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.

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

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

  9. В элементе DefaultItemValidator реализуйте ItemValidator и переопределите метод isValid() следующим образом.
    public class DefaultItemValidator implements ItemValidator {
    
        @Override
        public boolean isValid(Item item) {
            return item.getValue() < item.getLimit();
        }
    }

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

  10. Нажмите кнопку "Выполнить проект" (Кнопка "Выполнить проект") на главной панели инструментов среды IDE. Файл скомпилирован и развернут в GlassFish, и страница приветствия приложения (process.xhtml) отображается в веб-браузере.
  11. Нажмите кнопку Выполнить на странице. Вернитесь в среду IDE и проверьте протокол сервера GlassFish. Протокол сервера отображается в окне вывода (Ctrl-4; ⌘-4 на Mac) на вкладке GlassFish. В нем видно, что элементы проверяются и перечисляются только допустимые элементы, значение которых меньше предельного.
    INFO: Item =  [Value=34, Limit=7] valid = false
    INFO: Item =  [Value=4, Limit=37] valid = true
    INFO: Item =  [Value=24, Limit=19] valid = false
    INFO: Item =  [Value=89, Limit=32] valid = false
    Окно вывода — протокол сервера GlassFish
  12. Теперь рассмотрим случай, в котором вам необходимо выполнить развертывание в другом месте, менее жестком, считающим компонент недопустимым только в том случае, если его значение более чем в два раза превышает ограничение. Может потребоваться другой компонент для реализации интерфейса ItemValidator для данной логики.

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

  13. Сделайте RelaxedItemValidator реализацией ItemValidator и переопределите метод isValid() следующим образом.
    public class RelaxedItemValidator implements ItemValidator {
    
        @Override
        public boolean isValid(Item item) {
            return item.getValue() < (item.getLimit() * 2);
        }
    }

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

  14. Нажмите кнопку "Выполнить проект" (Кнопка "Выполнить проект") для выполнения проекта. Обратите внимание, что теперь развертывание проекта завершается сбоем.
  15. Просмотрите протокол сервера в окне вывода (Ctrl-4; ⌘-4 на Mac). В протоколе отображается сообщение об ошибке неоднозначной зависимости. Это происходит по причине того, что имеются два кдласса, реализующих один и тот же интерфейс.
    org.glassfish.deployment.common.DeploymentException: точка внедрения имеет неоднозначные зависимости.
    Точка внедрения: field exercise2.ItemProcessor.itemValidator;
    Квалификаторы: [@javax.enterprise.inject.Default()];
    Возможные зависимости: [exercise3.RelaxedItemValidator, exercise3.DefaultItemValidator]

    Реализация Weld CDI не способна определить элемент, используемый для данной точки внедрения (RelaxedItemValidator или DefaultItemValidator).

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

  16. Добавьте аннотацию @Alternative и соответствующий оператор импорта в RelaxedItemValidator и DefaultItemValidator.

    Откройте в редакторе RelaxedItemValidator и внесите следующие изменения.
    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.

    Всплывающее окно с документацией Javadoc в редакторе

    Переключитесь к DefaultItemValidator (нажмите сочетание клавиш CTRL+TAB) и внесите следующее изменение.

    import javax.enterprise.inject.Alternative;
    ...
    
    @Alternative
    public class DefaultItemValidator implements ItemValidator {
    
        public boolean isValid(Item item) {
            return item.getValue() < item.getLimit();
        }
    }

    Выполняя развертывание приложения сейчас, вы получите ошибку "неудовлетворенная зависимость", так как два подходящих компонента были определены как альтернативные, но ни один из них не был активирован в файле beans.xml.

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

  19. Нажмите кнопку "Выполнить проект" (Кнопка "Выполнить проект") для запуска проекта (или нажмите клавишу F6; сочетание клавиш fn+F6 в Mac OS). В обозревателе нажмите кнопку 'Выполнить' на отображаемой странице. Переключитесь обратно на среду IDE и просмотрите протокол сервера GlassFish, отображающийся в окне вывода (Ctrl-4; ⌘-4 на Mac).
    INFO: Item =  [Value=34, Limit=7] valid = false
    INFO: Item =  [Value=4, Limit=37] valid = true
    INFO: Item =  [Value=24, Limit=19] valid = true
    INFO: Item =  [Value=89, Limit=32] valid = false

    Используется реализация RelaxedItemValidator, а третий элемент отображается как верный, хотя значение (24) больше заданного предела (19).


Применение аннотаций жизненного цикла к управляемым компонентам

В этом упражнении ItemErrorHandler будет добавлен в главный класс ItemProcessor. Для добавления выбран FileErrorReporter, поскольку он является единственной реализацией интерфейса ItemErrorHandler. Для настройки зависящих от жизненного цикла действий класса следует использовать аннотации @PostConstruct и @PreDestroy из спецификации управляемых компонентов (включенной в JSR 316: спецификация платформы Java, Enterprise Edition 6).

Схема CDI для создаваемых в этом упражнении объектов

После этого необходимо создать интерфейс ItemErrorHandler для обработки обнаруженных недопустимых элементов.

  1. В окне "Проекты" щелкните правой кнопкой пакет exercise3 и выберите команду "Создать" > "Интерфейс Java".
  2. В мастере интерфейсов Java введите имя класса ItemErrorHandler и имя пакета exercise3. Нажмите кнопку "Готово".

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

  3. Добавьте метод handleItem(), принимающий параметр типа Item.
    public interface ItemErrorHandler {
        void handleItem(Item item);
    }

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

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

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

  5. Сделайте FileErrorReporter реализацией ItemErrorHandler и переопределите метод handleItem() следующим образом.
    public class FileErrorReporter implements ItemErrorHandler {
    
        @Override
        public void handleItem(Item item) {
            System.out.println("Saving " + item + " to file");
        }
    }

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

    Вам нужно открыть файл до начала обработки элементов, оставить его открытым в течение процесса добавления содержимого в данный файл, а затем закрыть его по завершении процесса. Можно вручную добавить методы initProcess() и finishProcess() к компоненту средства сообщения об ошибке, но в этом случае вы не сможете выполнить кодирование интерфейса, так как вызывающей стороне будет необходимо знать данные специфичные для класса методы. Можно добавить те же методы к интерфейсу ItemErrorReporter, но в этом случае потребуется выполнить ненужное внедрение данных методов в каждый класс, реализующий данный интерфейс. Вместо этого можно использовать несколько аннотаций жизненного цикла из спецификации управляемых компонентов (входящей в JSR 316: спецификация платформы Java, Enterprise Edition 6) для вызова методов в компоненте в конкретных точках жизненного цикла компонента. Метод с аннотацией @PostConstruct вызывается после создания компонента и учета всех его зависимостей. Метод с аннотацией @PreDestroy аналогичным образом вызывается непосредственно перед удалением компонента контейнером.

  6. Добавьте следующие методы init() и release() с аннотациями @PostConstruct и @PreDestroy.
    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");
        }
    }
  7. Исправьте операторы импорта. Щелкните правой кнопкой мыши редактор и выберите "Исправить операторы импорта" или нажмите Ctrl-Shift-I (⌘-Shift-I на Mac). В начало файла добавляются операторы импорта для javax.annotation.PostConstruct и javax.annotation.PreDestroy.
  8. После этого добавьте новый компонент ItemErrorHandler к ItemProcessor.
    @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.

  9. Нажмите кнопку "Выполнить проект" (Кнопка "Выполнить проект") для запуска проекта (или нажмите клавишу F6; сочетание клавиш fn+F6 в Mac OS). В обозревателе нажмите кнопку 'Выполнить', отображаемую на странице. Переключитесь обратно на среду IDE и просмотрите протокол сервера GlassFish, отображающийся в окне вывода (Ctrl-4; ⌘-4 на Mac).
    INFO: Creating file error reporter
    INFO: Saving  [Value=34, Limit=7] to file
    INFO: Saving  [Value=89, Limit=32] to file
    INFO: Closing file error reporter

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

Различные развертывания приложений могут использовать различные правила обработки недопустимых элементов: отклонение элементов, отправку уведомлений, выделение элементов или перечисление их в выходном файле. Кроме того, может потребоваться комбинация этих действий (например, отклонить заказ, отправить письмо менеджеру и записать заказ в файл). Оптимальным способом обработки такой многогранной проблемы является использование событий. События CDI рассматриваются в последнем примере этой серии.

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