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

@Decorator

public class FromEightToThirteenDigitsDecorator implements NumberGenerator {

··@Inject @Delegate

··private NumberGenerator numberGenerator;

··public String generateNumber() {

····String issn = numberGenerator.generateNumber();

····String isbn = "13-84356" + issn.substring(1);

····returnisbn;

··}

}

Декораторы должны иметь точку внедрения делегата (аннотированную @Delegate) такого же типа, как и компоненты, которые они декорируют (здесь интерфейс NumberGenerator). Это позволяет объекту вызывать объект-делегат (например, целевой компонент IssnNumberGenerator), а затем, в свою очередь, вызывать на него любой бизнес-метод (например, numberGenerator.generateNumber() в листинге 2.34).

По умолчанию все декораторы отключены, как и альтернативы с перехватчиками. Декораторы необходимо активизировать в файле beans.xml, как показано в листинге 2.35.

Листинг 2.35. Активизация декоратора в дескрипторе развертывания beans.xml

<beansxmlns="http://xmlns.jcp.org/xml/ns/javaee"

·············xmlns: xsi="http://www.w3.org/2001/XMLSchema-instance"

·············xsi: schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 

··································http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"

·············version="1.1" bean-discovery-mode="all">

··<decorators>

····<class>org.agoncal.book.javaee7.chapter02.FromEightToThirteenDigitsDecorator</class>

··</decorators>

</beans>

Если в приложении присутствуют и перехватчики, и декораторы, то перехватчики вызываются в первую очередь.

События

Внедрение зависимостей, перехватчики и декораторы гарантируют слабую связанность, обеспечивая разнообразные варианты дополнительного поведения как во время развертывания, так и во время выполнения. События, кроме того, позволяют компонентам взаимодействовать вне зависимости от времени компиляции. Один компонент может определить событие, другой — инициировать событие, а третий — обработать его. Эта базовая схема следует шаблону проектирования «Наблюдатель» (Observer), разработанному группой Gang of Four.

Производители событий запускают события, используя интерфейс javax.enterprise.event. Производитель инициирует события вызовом метода fire(), передает объект события и не зависит от наблюдателя. В листинге 2.36 BookService запускает событие (bookAddedEvent) каждый раз при создании книги. Код bookAddedEvent.fire(book) инициирует событие и оповещает любые методы наблюдателя, следящие за этим конкретным событием. Содержание этого события — сам объект Book, который будет передан от производителя потребителю.

Листинг 2.36. BookService запускает событие каждый раз, когда создается книга

public class BookService {

··@Inject

··private NumberGenerator numberGenerator;

··@Inject

··private Event<Book> bookAddedEvent;

··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;

··}

}

События инициируются производителем событий и на них подписываются наблюдатели. Наблюдатель — это компонент с одним или несколькими «отслеживающими» методами. Каждый из этих методов наблюдателя берет в качестве параметра событие определенного типа, сопровождаемое аннотацией @Observers и опциональными квалификаторами. Метод наблюдателя оповещается о событии, если объект события соответствует типу и всем квалификаторам. В листинге 2.37 показана служба инвентаризации, задача которой — отслеживать новые книжные поступления, дополняя информацию о книжном фонде. Там используется метод addBook, который наблюдает за каждым событием с типом Book. Аннотированный параметр называется параметром события. Поэтому, как только событие инициируется компонентом BookService, контейнер CDI приостанавливает выполнение и передает событие любому зарегистрированному наблюдателю. В нашем случае в листинге 2.37 будет вызван метод addBook, который обновит список книг, а затем контейнер продолжит выполнение кода с того места, где он остановился в компоненте BookService. Это означает, что события в CDI не рассматриваются асинхронно.

Листинг 2.37. InventoryService наблюдает за событием Book

public class InventoryService {

··@Inject

··private Logger logger;

··List<Book> inventory = new ArrayList<>();

··public void addBook(@Observes Book book) {

····logger.info("Adding book " + book.getTitle() + "to inventory");

····inventory.add(book);

··}

}

Как и большинство CDI, производство события и подписка являются типобезопасными и позволяют квалификаторам определять, какие наблюдатели событий будут использоваться. Событию может быть назначен один или несколько квалификаторов (с членами либо без таковых), которые позволяют наблюдателям отличить его от остальных событий такого же типа. Листинг 2.38 возвращается к компоненту BookService, добавив туда дополнительное событие. При создании книги инициируется событие bookAddedEvent, а при удалении — событие bookRemovedEvent, оба с типом Book. Чтобы можно было отличать эти события, каждое из них сопровождается аннотацией @Added или @Removed. Код этих квалификаторов идентичен коду в листинге 2.7: аннотация без членов и аннотированная @Qualifier.