public class BookService {
··@Inject
··private NumberGenerator numberGenerator;
··@Inject @Added
··private Event<Book> bookAddedEvent;
··@Inject @Removed
··private Event<Book> bookRemovedEvent;
··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<Book> inventory = new ArrayList<>();
··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:
• src/main/java — каталог для всех компонентов, квалификаторов, перехватчиков и класса Main;
• src/main/resources — пустой файл beans.xml, поэтому мы можем инициировать CDI без альтернатив и перехватчиков;
• src/test/java — каталог для интеграционных тестов BookServiceIT и альтернативы MockGenerator;
• src/test/resources — файл beans.xml, обеспечивающий работу альтернативы MockGenerator и перехватчика LoggingInterceptor;
• pom.xml — модель объекта проекта Maven (POM), описывающая проект и его зависимости.
Написание классов Book и BookService
Приложение CD-Bookstore использует класс BookService для создания книг (листинг 2.40). Java-объект Book (листинг 2.41) имеет название, описание и цену. Номер книги (ISBN или ISSN) генерируется внешним сервисом.