Управляемые компоненты — это объекты, которые управляются контейнером и поддерживают только небольшой набор базовых сервисов: внедрение ресурса, управление жизненным циклом и перехват. Они появились в Java EE 6, обеспечив более легковесную компонентную модель, приведенную в соответствие с остальной частью платформы Java EE. Они дают общее основание различным типам компонентов, существующих в платформе Java EE. Например, Enterprise JavaBean может рассматриваться как управляемый компонент с дополнительными сервисами. Сервлет также может считаться управляемым компонентом с дополнительными сервисами (отличным от EJB) и т. д.
Компоненты — это объекты CDI, основанные на базовой модели управляемых компонентов. Они имеют улучшенный жизненный цикл для объектов с сохранением состояния; привязаны к четко определенным контекстам; обеспечивают сохранение безопасности типов при внедрении зависимостей, перехвате и декорации; специализируются с помощью аннотаций квалификатора; могут использоваться в языке выражений (EL). По сути, с очень малым количеством исключений потенциально каждый класс Java, имеющий конструктор по умолчанию и исполняемый внутри контейнера, является компонентом. Поэтому компоненты JavaBeans и Enterprise JavaBeans также могут воспользоваться преимуществами этих сервисов CDI.
Внедрение зависимостей (DI) — это шаблон разработки, в котором разделяются зависимые компоненты. Здесь мы имеем дело с инверсией управления, причем инверсии подвергается процесс получения необходимой зависимости. Этот термин был введен Мартином Фаулером. Внедрение зависимостей в такой управляемой среде, как Java EE, можно охарактеризовать как полную противоположность применения интерфейса JNDI. Объекту не приходится искать другие объекты, так как контейнер внедряет эти зависимые сущности без вашего участия. В этом состоит так называемый принцип Голливуда: «Не звоните нам (не ищите объекты), мы сами вам позвоним (внедрим объекты)».
Java EE была создана в конце 1990-х годов, и в самой первой версии уже присутствовали компоненты EJB, сервлеты и служба JMS. Эти компоненты могли использовать JNDI для поиска ресурсов, управляемых контейнером, таких как интерфейс JDBC DataSource, JMS-фабрики либо адреса назначения. Это сделало возможной зависимость компонентов и позволило EJB-контейнеру взять на себя сложности управления жизненным циклом ресурса (инстанцирование, инициализацию, упорядочение и предоставление клиентам ссылок на ресурсы по мере необходимости). Однако вернемся к теме внедрения ресурса, выполняемого контейнером.
В платформе Java EE 5 появилось внедрение ресурсов для разработчиков. Это позволило им внедрять такие ресурсы контейнера, как компоненты EJB, менеджер сущностей, источники данных, фабрики JMS и адреса назначения, в набор определенных компонентов (сервлетов, связующих компонентов JSF и EJB). С этой целью Java EE 5 предоставила новый набор аннотаций (@Resource, @PersistenceContext, @PersistenceUnit, @EJB и @WebServiceRef).
Новшеств Java EE 5 оказалось недостаточно, и тогда Java EE 6 создала еще две спецификации для добавления в платформу настоящего внедрения зависимостей (DI): Dependency Injection (запрос JSR 330) и Contexts and Dependency Injection (запрос JSR 299). На сегодняшний день в Java EE 7 внедрение зависимостей используется еще шире: для связи спецификаций.
Управление жизненным циклом
Жизненный цикл POJO достаточно прост: вы, Java-разработчик, создаете экземпляр класса, используя ключевое слово new, и ждете, пока сборщик мусора (Garbage Collector) избавится от него и освободит некоторое количество памяти. Но если вы хотите запустить компонент CDI внутри контейнера, вам нельзя указывать ключевое слово new. Вместо этого вам необходимо внедрить компонент, а все остальное сделает контейнер. Тут подразумевается, что только контейнер отвечает за управление жизненным циклом компонента: сначала он создает экземпляр, затем избавляется от него. Итак, как же вам инициализировать компонент, если вы не можете вызвать конструктор? В этом случае контейнер дает вам указатель после конструкции экземпляра и перед его уничтожением.
Рисунок 2.1 показывает жизненный цикл управляемого компонента (следовательно, и компонента CDI). Когда вы внедряете компонент, только контейнер (EJB, CDI или веб-контейнер) отвечает за создание экземпляра (с использованием кодового слова new). Затем он разрешает зависимости и вызывает любой метод с аннотацией @PostConstruct до первого вызова бизнес-метода на компоненте. После этого оповещение с помощью обратного вызова @PreDestroy сигнализирует о том, что экземпляр удаляется контейнером.
Читать дальше