Выбрать главу

public class BookService {

@Inject

private NumberGenerator numberGenerator;

··@Inject

··private EntityManager em;

··private Date instanciationDate;

··@PostConstruct

··private void initDate() {

····instanciationDate = new Date();

··}

··@Transactional

··public Book createBook(String title, Float price, String description) {

····Book book = new Book(title, price, description);

····book.setIsbn(numberGenerator.generateNumber());

····book.setInstanciationDate(instanciationDate);

····em.persist(book);

····return book;

··}

}

Внутренняя организация компонента CDI

В соответствии со спецификацией CDI 1.1 контейнер распознает как компонент CDI любой класс, если:

• он не относится к нестатичным внутренним классам;

• это конкретный класс либо класс, имеющий аннотацию @Decorator;

• он имеет задаваемый по умолчанию конструктор без параметров либо объявляет конструктор с аннотацией @Inject.

Компонент может иметь опциональную область видимости, опциональное EL-имя (EL — язык выражений), набор связок с перехватчиком и опциональное управление жизненным циклом.

Внедрение зависимостей

Java относится к объектно-ориентированным языкам программирования. Это означает, что реальный мир отображается с помощью объектов. Класс Book отображает копию H2G2, Customer замещает вас, а PurchaseOrder замещает то, как вы покупаете эту книгу. Эти объекты зависят друг от друга: книга может быть прочитана покупателем, а заказ на покупку может относиться к нескольким книгам. Такая зависимость — одно из достоинств объектно-ориентированного программирования.

Например, процесс создания книги (BookService) можно сократить до инстанцирования объекта Book, сгенерировав уникальный номер с использованием другого сервиса (NumberGenerator), сохраняющий книгу в базу данных. Сервис NumberGenerator может сгенерировать номер ISBN из 13 цифр либо ISBN более старого формата из восьми цифр, известный как ISSN. BookService затем будет зависеть от IsbnGenerator либо IssnGenerator. Тот или иной вариант определяется условиями работы или программным окружением.

Рисунок 2.3 демонстрирует схему класса интерфейса NumberGenerator, который имеет один метод (String generateNumber()) и реализуется посредством IsbnGenerator и IssnGenerator. Bookservice зависит от интерфейса при генерации номера книги.

Рис. 2.3. Схема класса с интерфейсом NumberGenerator и реализациями

Как бы вы соединили BookService с ISBN-реализацией интерфейса NumberGenerator? Одно из решений — использовать старое доброе ключевое слово new, как показано в листинге 2.2.

Листинг 2.2. POJO-объект BookService, создающий зависимости с использованием ключевого слова new

public class BookService {

··private NumberGenerator numberGenerator;

··public BookService() {

····this.numberGenerator = new IsbnGenerator();

··}

··public Book createBook(String title, Float price, String description) {

····Book book = new Book(title, price, description);

····book.setIsbn(numberGenerator.generateNumber());

····return book;

··}

}

Код в листинге 2.2 достаточно прост и выполняет необходимые действия. BookService создает в конструкторе экземпляр IsbnGenerator, который затем влияет на атрибут numberGenerator. Вызов метода numberGenerator.generateNumber() сгенерирует номер из 13 цифр.

Но что, если вы хотите выбирать между реализациями, а не просто привязываться к IsbnGenerator? Одно из решений — передать реализацию конструктору и предоставить внешнему классу возможность выбирать, какую реализацию использовать (листинг 2.3).

Листинг 2.3. Объект POJO BookService, выбирающий зависимости с использованием конструктора

public class BookService {

··private NumberGenerator numberGenerator;

··public BookService(NumberGenerator numberGenerator) {

····this.numberGenerator = numberGenerator;

··}

··public Book createBook(String title, Float price, String description) {

····Book book = new Book(title, price, description);

····book.setIsbn(numberGenerator.generateNumber());

····return book;

··}

}

Таким образом, внешний класс смог использовать BookService с необходимой реализацией.

BookService bookService = new BookService(new IsbnGenerator())

BookService bookService = new BookService(new IssnGenerator())

Этот пример иллюстрирует инверсию управления: инвертируется управление созданием зависимости (а не сам класс) между BookService и NumberGenerator, так как оно дается внешнему классу. Поскольку в конце вы соединяете зависимости самостоятельно, эта техника называется конструированием вручную. В предыдущем примере кода мы использовали конструктор для выбора реализации (внедрение конструктора), но еще один привычный способ состоит в использовании сеттеров (внедрение сеттера). Однако вместо конструирования зависимостей вручную вы можете перепоручить эту задачу механизму внедрения (например, CDI).