Простая Java, CDI и EJB — это базовые бизнес-компоненты в современном приложении на Java EE. Почему они называются базовыми бизнес-компонентами? Как уже отмечалось, в первую очередь мы обращаем внимание на реальные коммерческие задачи. Существуют компоненты и функции, нацеленные на решение основных бизнес-задач, тогда как другие просто поддерживают, делают их доступными или реализуют иные технические требования.
Java EE поставляется в комплекте с различными API, которые поддерживают десятки технических требований. Большинство из них технически обоснованы. Однако самым большим преимуществом платформы Java EE является то, что чистая бизнес-логика на Java может быть реализована с минимальным воздействием технологических деталей на код. Обычно для этого достаточно CDI и EJB. Остальные API, необходимые по техническим соображениям, такие как JPA, JAX-RS, JSON-P и многие другие, вводятся по мере необходимости.
Управляемые объекты независимо от типа — CDI или EJB — реализованы в виде аннотированных классов Java, без каких-либо технических суперклассов или интерфейсов. Раньше это называлось представлением без интерфейса. В настоящее время этот вариант применяется по умолчанию. Расширение классов усложняет представление предметной области, не говоря уже о других недостатках, которые проявляются при тестировании. Современный фреймворк интегрируется максимально простым и незаметным способом.
EJB и CDI: общее и различия
Зададимся вопросом: EJB или CDI? Какие управляющие объекты применять и когда?
Вообще говоря, у EJB больше функциональных возможностей, готовых к использованию. Управляемые объекты CDI представляют собой несколько упрощенную альтернативу. Каковы основные различия между этими технологиями и как они влияют на работу программиста?
Первое различие — это области видимости. Объекты сессии EJB либо не сохраняют состояние, то есть остаются активными в течение всего клиентского запроса, либо сохраняют состояние, то есть остаются активными в течение времени жизни HTTP-сессии клиента, либо являются синглтонами. Управляемые объекты CDI имеют дополнительные возможности, такие как добавление нестандартных областей действия, а область видимости, выбираемая по умолчанию, является зависимой — она активна в зависимости от времени жизни ее точки внедрения. Подробнее об этом читайте в подразделе «Области видимости».
Другое различие между управляемыми объектами EJB и CDI заключается в том, что EJB-объекты неявно решают определенные сквозные задачи, такие как мониторинг, транзакции, обработка исключений и управление параллелизмом для синглтонов. Например, при вызове бизнес-метода EJB неявно запускает техническую транзакцию, которая остается активной в течение всего времени выполнения метода и объединяет источники данных или внешние системы.
Кроме того, EJB без сохранения состояния после использования объединяются. Это означает, что после вызова бизнес-метода для объекта без сохранения состояния сущность этого объекта может и будет применяться повторно из контейнера. Благодаря этому EJB-объекты немного эффективнее, чем CDI, которые заново создаются каждый раз, когда их область видимости требует этого.
На практике технические различия не очень сильно влияют на работу программиста. За исключением применения разных аннотаций, обе технологии могут использоваться в одном и том же стиле. Java EE движется к возможности предоставлять более открытый выбор из этих двух вариантов. Например, начиная с версии Java EE 8, можно обрабатывать асинхронные события исключительно с помощью CDI, а не только привлекая EJB-объекты.
Однако интеграция функциональности от CDI — одна из самых серьезных возможностей API Java EE. Уже только внедрение зависимостей, CDI-генераторы и события являются эффективными средствами для решения различных задач.
Главная, наиболее широко применяемая функция CDI — это внедрение зависимостей с помощью аннотации @Inject
. Оно реализовано таким образом, что программисту не нужно задумываться, какая технология Java EE управляет объектами, — она просто работает. Вы можете смешивать и комбинировать CDI-объекты и EJB с любыми областями видимости — фреймворк сам позаботится о том, какие объекты создаются или используются в данной области видимости. Это обеспечивает гибкость, например, когда объекты с более короткой областью видимости внедряются в объекты с более длинной областью видимости — допустим, когда объект с областью видимости в пределах сессии внедряется в синглтон.
Данная функция поддерживает бизнес-задачи таким образом, что контуры и элементы управления могут просто внедрять нужные зависимости, не занимаясь их созданием или управлением ими.
В следующем коде показано, как контур, реализованный в виде объекта сессии без сохранения состояния, внедряет необходимые элементы управления:
import javax.ejb.Stateless;
import javax.inject.Inject;
@Stateless
public class CarManufacturer {
@Inject
CarFactory carFactory;
@Inject
CarStorage carStorage;
public Car manufactureCar (Specification spec) {
Car car = carFactory.createCar (spec); carStorage.store (car);
return car;
}
}
Класс CarManufacturer
представляет собой EJB-объект без сохранения состояния. Внедренные компоненты CarFactory и CarStorage реализованы в виде CDI-объектов с зависимыми областями видимости, которые будут созданы и внедрены в EJB-объект. Платформа Java EE упрощает устранение зависимостей, позволяя с помощью аннотации @Inject вставлять любые объекты, необходимые для проекта. Но так было не всегда — прежде для внедрения EJB-объектов задействовалась аннотация @EJB
. Аннотация @Inject
упрощает использование объектов в Java EE.
Внимательные читатели могли заметить, что внедрение через поле с областями видимости осуществляется внутри пакета Java. Внедрение через поле меньше влияет на содержимое класса, поскольку позволяет не обращаться к его конструктору. Видимость внутри пакета дает программистам возможность создавать и внедрять зависимости в тестовой области видимости.
Генераторы CDI
Генераторы CDI — это еще одна функция Java EE, которая особенно полезна для реализации всевозможных фабрик. Генераторы, в основном реализуемые как методы, создают объект, который можно внедрять в другие управляемые объекты. Это отделяет логику создания и конфигурации от использования. Генераторы полезны в случаях, когда нужно вводить дополнительные типы, помимо тех, которые внедряются управляемыми объектами.
Далее показано определение метода-генератора CDI:
import javax.enterprise.inject.Produces;
public class CarFactoryProducer {
@Produces
public CarFactory exposeCarFactory() {
CarFactory factory = new BMWCarFactory();
// дополнительная логика
return factory;
}
}
Открытый тип CarFactory
можно ввести просто с помощью внедрения аннотацией @Inject
, как было показано ранее в примере с CarManufacturer
. Каждый раз, когда нужна сущность CarFactory
, CDI-объект вызывает метод exposeCarFactory()
и вставляет возвращаемый объект в точку внедрения.
Этих методов достаточно для большинства случаев использования основной бизнес-логики.
Генерация событий предметной области
Для тех случаев, когда необходимо еще больше отделить бизнес-логику от управляемых объектов, в CDI предусмотрена функция обработки событий. Управляемые объекты могут активизировать объекты событий, которые затем действуют как полезная нагрузка и обрабатываются наблюдателями событий. Активизируя и обрабатывая события CDI, мы отделяем основную бизнес-логику от побочных аспектов обработки событий. Этот прием применяется, в частности, в тех случаях, когда в бизнес-логике уже продумана концепция событий.
По умолчанию события CDI обрабатываются синхронно, прерывая выполнение в том месте, где они были активизированы. Они также могут обрабатываться асинхронно или в определенных точках жизненного цикла технической транзакции.
На примере следующего кода показано, как определять и активизировать события CDI в бизнес-сценарии:
import javax.enterprise.event.Event;
@Stateless
public class CarManufacturer {
@Inject
CarFactory carFactory;
@Inject
Event<CarCreated> carCreated;
public Car manufactureCar(Specification spec) {
Car car = carFactory.createCar(spec);
carCreated.fire(new CarCreated(spec));
return car;
}
}
Событие CarCreated
является неизменяемым и содержит относящуюся к событию предметной области информацию, такую как спецификация автомобиля. Само событие обрабатывается в классе CreatedCarListener
, который находится в следующем пакете управления:
import javax.enterprise.event.Observes;
public class CreatedCarListener {
public void onCarCreated(@Observes CarCreated event) {
Specification spec = event.getSpecification();
// обработка события
}
}
Таким образом, обработчик события отделен от основной бизнес-логики. Контейнер CDI сам обеспечит подключение функций обработки событий и синхронный вызов метода onCarCreated()
.
Подробнее об активизации и обработке событий асинхронно или в определенных точках жизненного цикла транзакции читайте в разделе «Последовательность выполнения».
События CDI — это способ отделить определение событий предметной области от их обработки. Тогда можно изменить или усилить логику обработчика событий, не затрагивая компонент manufactureCar
.
Области видимости
Область видимости управляемых объектов очень важна в тех случаях, когда состояние сохраняется в приложении дольше чем на протяжении одного запроса.
Если весь бизнес-процесс можно реализовать без сохранения состояния, просто выполняя некоторую логику и затем отбрасывая все состояния, то определить область видимости очень легко. В большинстве случаев для этого достаточно объектов сессии без сохранения состояния и зависимых от области видимости объектов CDI.
Области видимости синглтона EJB и приложения CDI также довольно часто используются. Отдельные сущности управляемых объектов предоставляют простой способ хранения или кэширования информации с длительным временем жизни. Наряду со сложной технологией кэширования, синглтон, содержащий простые наборы или отображения с контролем параллелизма, по-прежнему остается простейшим способом разработки кратковременных хранилищ данных, ориентированных на решение конкретных задач. Синглтоны также обеспечивают единую точку запуска функций, доступ к которым по определенным причинам должен быть ограничен.
И последняя область видимости управляемых объектов EJB и CDI — это область сессии, привязанная к HTTP-сессии клиента. Объекты из этой области остаются активными и могут быть повторно использованы вместе со всеми своими состояниями, пока активна сессия пользователя. Однако при хранении данных сессии в объектах с сохранением состояния возникает проблема, когда клиенты заново подключаются к тому же серверу приложений.
Это, безусловно, возможно, но не позволяет создавать приложения без сохранения состояния, которыми было бы легко управлять. Если приложение становится недоступным, то все временные данные сессии теряются. В современных корпоративных приложениях в целях оптимизации состояние обычно хранится в базе данных или кэшах. Поэтому объекты с видимостью в пределах сессии применяются все реже.
Управляемые объекты CDI имеют больше готовых областей видимости, а именно область видимости в пределах обмена данными и зависимую область видимости (используется по умолчанию). Существует также возможность создания нестандартных областей видимости для особых требований. Однако опыт показывает, что встроенных областей видимости достаточно для большинства корпоративных приложений. Подробная информация о том, как расширить платформу и разработать дополнительные области видимости, содержится в спецификации CDI.
Как мы уже видели, с помощью основных компонентов Java EE можно достичь очень многого. Прежде чем приступать к изучению технологий интеграции, таких как HTTP-коммуникации и доступ к базе данных, более подробно рассмотрим основные шаблоны, применяемые при проектировании предметной области.