····logger.info("Adding book " + book.getTitle() + "to inventory");
····inventory.add(book);
··}
}
Как и большинство CDI, производство события и подписка являются типобезопасными и позволяют квалификаторам определять, какие наблюдатели событий будут использоваться. Событию может быть назначен один или несколько квалификаторов (с членами либо без таковых), которые позволяют наблюдателям отличить его от остальных событий такого же типа. Листинг 2.38 возвращается к компоненту BookService, добавив туда дополнительное событие. При создании книги инициируется событие bookAddedEvent, а при удалении — событие bookRemovedEvent, оба с типом Book. Чтобы можно было отличать эти события, каждое из них сопровождается аннотацией @Added или @Removed. Код этих квалификаторов идентичен коду в листинге 2.7: аннотация без членов и аннотированная @Qualifier.
public class BookService {
··@Inject
··private NumberGenerator numberGenerator;
··@Inject @Added
··private Event
··@Inject @Removed
··private Event
··public Book createBook(String title, Float price, String description) {
····Book book = new Book(title, price, description);
····book.setIsbn(numberGenerator.generateNumber());
····bookAddedEvent.fire(book);
····return book;
··}
··public void deleteBook(Book book) {
····bookRemovedEvent.fire(book);
··}
}
InventoryService в листинге 2.39 наблюдает за обоими событиями, объявив два отдельных метода, один из которых наблюдает за событием о добавлении книги (@Observes @Added Book), а другой — за событием о ее удалении (@Observes @Removed Book).
public class InventoryService {
··@Inject
··private Logger logger;
··List
··public void addBook(@Observes @Added Book book) {
····logger.warning("Книга " + book.getTitle() + " добавлена в список");
····inventory.add(book);
··}
··public void removeBook(@Observes @Removed Book book) {
····logger.warning("Книга " + book.getTitle() + " удалена из списка");
····inventory.remove(book);
··}
}
Поскольку модель события использует квалификаторы, вам было бы целесообразно задавать поля квалификаторов или агрегировать их. Следующий код наблюдает за всеми добавленными книгами, цена которых превышает 100:
void addBook(@Observes @Added @Price(greaterThan=100) Book book)
Все вместе
А теперь совместим некоторые из этих понятий, напишем несколько компонентов, производителей, используем внедрение, квалификаторы, альтернативы и связывание с перехватчиком. В этом примере применяется контейнер Weld для запуска класса Main в Java SE, а также интеграционный тест для проверки правильности внедрения.
Рисунок 2.7 показывает схему со всеми классами, необходимыми для запуска этого образца кода, и описывает все точки внедрения.
Рис. 2.7. Все вместе
• Компонент BookService имеет метод для создания Java-объектов Book.
• Интерфейс NumberGenerator имеет две реализации для генерации номеров ISBN и ISSN (IsbnGenerator и IssnGenerator) и одну альтернативную реализацию, чтобы генерировать имитационные номера для интеграционных тестов (MockGenerator).
• Реализации NumberGenerator используют два квалификатора, чтобы избежать неоднозначного внедрения зависимости: @ThirteenDigits и @EightDigits.
• LoggingProducer делает возможным внедрение Logger благодаря методу-производителю. LoggingInterceptor в паре с перехватчиком Loggable позволяет компонентам CDI сохранять в журнал записи о методах.
• Класс Main использует BookService, чтобы создать Book и сгенерировать номер с помощью IsbnGenerator. Интеграционный тест BookServiceIT использует альтернативу MockGenerator для генерации имитационного номера книги.
Классы, описанные на рис. 2.7, следуют стандартной структуре каталога Maven: