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.
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).
public class BookService {
··private NumberGenerator numberGenerator;
··public BookService(NumberGenerator numberGenerator) {