Рис. 2.6.Вызов перехватываемого бизнес-метода
Примечание
Листинг 2.23 использует новую аннотацию @javax.transaction.Transactional. Она применяется для управления разграничением операций на компонентах CDI, а также сервлетах и оконечных точках сервисов JAX-RS и JAX-WS. Она обеспечивает семантику атрибутов транзакции EJB в CDI. Аннотация @Transactional реализуется с помощью перехватчика. Подробнее о транзакциях рассказывается в главе 9.
Листинг 2.23 определяет перехватчик, доступный только для CustomerService. Но чаще всего вам требуется изолировать сквозную функциональность в отдельный класс и сообщить контейнеру, чтобы он перехватил вызовы нескольких компонентов. Запись информации в журнал (логирование) — типичный пример ситуации, когда вам требуется, чтобы все методы всех ваших компонентов регистрировали сообщения о входе и выходе. Для указания перехватчика класса вам необходимо разработать отдельный класс и дать контейнеру команду применить его на определенном компоненте или методе компонента.
Чтобы обеспечить совместный доступ к коду множественным компонентам, возьмем методы logMethod() из листинга 2.23 и изолируем их в отдельный класс, как показано в листинге 2.24. Обратите внимание на метод init(), который сопровождается аннотацией @AroundConstruct и будет вызван только вместе с конструктором компонента.
Листинг 2.24. Класс перехватчика сAround-Invoke и Around-Construct
public class LoggingInterceptor {
··@Inject
··private Logger logger;
··@AroundConstruct
··private void init(InvocationContext ic) throws Exception {
····logger.fine("Entering constructor");
····try {
······ic.proceed();
····} finally {
······logger.fine("Exiting constructor");
····}
··}
··@AroundInvoke
··public Object logMethod(InvocationContext ic) throws Exception {
····logger.entering(ic.getTarget(). toString(), ic.getMethod(). getName());
····try {
······return ic.proceed();
····} finally {
······logger.exiting(ic.getTarget(). toString(), ic.getMethod(). getName());
····}
··}
}
Теперь LoggingInterceptor может быть прозрачно обернут любым компонентом, заинтересованным в этом перехватчике. Для этого компоненту необходимо сообщить контейнеру аннотацию @javax.interceptor.Interceptors. В листинге 2.25 аннотация задается методом createCustomer(). Это означает, что любой вызов этого метода будет перехвачен контейнером, и будет вызван класс LoggingInterceptor (регистрация сообщения на входе в метод и выходе из него).
Листинг 2.25. CustomerService использует перехватчик водном методе
@Transactional
public class CustomerService {
··@Inject
··private EntityManager em;
··@Interceptors(LoggingInterceptor.class)
··public void createCustomer(Customer customer) {
····em.persist(customer);
··}
··public Customer findCustomerById(Long id) {
····return em.find(Customer.class, id);
··}
}
В листинге 2.25 аннотация @Interceptors прикрепляется только к методу createCustomer(). Это означает, что, если клиент вызывает findCustomerById(), контейнер не будет перехватывать вызов. Если вы хотите, чтобы перехватывались вызовы обоих методов, можете добавить аннотацию @Interceptors либо сразу к обоим методам, либо к самому компоненту. Когда вы это делаете, перехватчик приводится в действие при вызове любого из методов. А поскольку перехватчик имеет аннотацию @AroundConstruct, вызов конструктора тоже будет перехвачен.
@Transactional
@Interceptors(LoggingInterceptor.class)
public class CustomerService {
··public void createCustomer(Customer customer) {…}
··public Customer findCustomerById(Long id) {…}
}
Если ваш компонент имеет несколько методов и вы хотите применить перехватчик ко всему компоненту за исключением определенного метода, можете использовать аннотацию javax.interceptor.ExcludeClassInterceptors для исключения перехвата вызова. В следующем отрывке кода вызов к updateCustomer() не будет перехвачен, а остальные будут:
@Transactional
@Interceptors(LoggingInterceptor.class)
public class CustomerService {
··public void createCustomer(Customer customer) {…}
··public Customer findCustomerById(Long id) {…}
··@ExcludeClassInterceptors
··public Customer updateCustomer(Customer customer) {… }
}
Перехватчик жизненного цикла
В начале этой главы я рассказывал о жизненном цикле управляемого компонента (см. рис. 2.2) и событиях обратного вызова. С помощью аннотации обратного вызова вы можете дать контейнеру команду вызвать метод в определенной фазе жизненного цикла (@PostConstruct и @PreDestroy). Например, если вы хотите вносить в журнал запись каждый раз, когда создается экземпляр компонента, вам просто нужно присоединить аннотацию @PostConstruct к методу вашего компонента и добавить к ней некоторые механизмы записи в журнал. Но что, если вам нужно перехватывать события жизненного цикла многих типов компонентов? Перехватчики жизненного цикла позволяют изолировать определенный код в отдельный класс и вызывать его, когда приводится в действие событие жизненного цикла.
Читать дальше