··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).
@Inject
Поскольку Java EE является управляемой средой, вам не придется конструировать зависимости вручную. Вместо вас ссылку может внедрить контейнер. Одним словом, внедрение зависимостей CDI — это возможность внедрять одни компоненты в другие с сохранением безопасности типов, что означает использование XML вместо аннотаций.
Внедрение уже существовало в Java EE 5 с такими аннотациями, как @Resource, @PersistentUnit и EJB. Но оно было ограничено до определенных ресурсов (баз данных, архитектура EJB) и компонентов (сервлетов, компонентов EJB, связующих компонентов JSF и т. д.). С помощью CDI вы можете внедрить практически что угодно и куда угодно благодаря аннотации @Inject. Обратите внимание, что в Java EE 7 разрешено использовать другие механизмы внедрения (@Resource), однако лучше стараться применять @Inject везде, где это возможно (см. подраздел «Производители данных» раздела «Создание компонента CDI» этой главы).
Листинг 2.4 показывает, как внедрять в BookService ссылку на NumberGenerator с помощью объекта CDI.
Листинг 2.4. BookService, использующий @Inject для получения ссылки наNumberGenerator
public class BookService {
··@Inject
··private NumberGenerator numberGenerator;
··public Book createBook(String title, Float price, String description) {
····Book book = new Book(title, price, description);
····book.setIsbn(numberGenerator.generateNumber());
····return book;
··}
}
Как видно из листинга 2.4, простая аннотация @Inject у атрибута сообщит контейнеру, что ему необходимо внедрить ссылку на реализацию NumberGenerator, относящуюся к свойству NumberGenerator. Это называется точкой внедрения (место, где находится аннотация @Inject). Листинг 2.5 показывает реализацию IsbnGenerator. Как видите, здесь нет специальных аннотаций, а класс реализует интерфейс NumberGenerator.
Листинг 2.5. Компонент IsbnGenerator
public class IsbnGenerator implements NumberGenerator {
··public String generateNumber() {
····return "13-84356-" + Math.abs(new Random(). nextInt());
··}
}
Точки внедрения
Во время инстанцирования компонента происходит внедрение в той точке, которая указана в аннотации @Inject. Внедрение может происходить с помощью трех различных механизмов: свойства, сеттера или конструктора.
Во всех предыдущих примерах кода вы видели аннотацию @Inject к атрибутам (свойствам):
@Inject
private NumberGenerator numberGenerator;
Обратите внимание, что не обязательно создавать метод геттера и сеттера для атрибута, чтобы использовать внедрение. CDI может получить прямой доступ к полю с внедренным кодом (даже если оно приватное), что иногда помогает исключить лишний код. Но вместо аннотирования атрибутов вы можете добавить аннотацию @Inject к конструктору, как показано ниже:
Читать дальше