Листинг 2.26 демонстрирует класс ProfileInterceptor с двумя методами: logMethod(), который используется для постконструкции, и profile(), применяемый для перехвата методов (@AroundInvoke).
Листинг 2.26. Перехватчик сжизненным циклом и Around-Invoke
public class ProfileInterceptor {
··@Inject
··private Logger logger;
··@PostConstruct
··public void logMethod(InvocationContext ic) throws Exception {
····logger.fine(ic.getTarget(). toString());
····try {
······ic.proceed();
····} finally {
······logger.fine(ic.getTarget(). toString());
····}
··}
··@AroundInvoke
··public Object profile(InvocationContext ic) throws Exception {
····long initTime = System.currentTimeMillis();
····try {
······return ic.proceed();
····} finally {
······long diffTime = System.currentTimeMillis() — initTime;
······logger.fine(ic.getMethod() + " took " + diffTime + " millis");
····}
··}
}
Как видно из листинга 2.26, перехватчики жизненного цикла берут параметр InvocationContext и вместо Object возвращают void. Чтобы применить перехватчик, определенный в листинге 2.26, компонент CustomerService (листинг 2.27) должен использовать аннотацию @Interceptors и определять ProfileInterceptor. Если компонент инстанцируется контейнером, метод logMethod() будет вызван раньше метода init(). Затем, если клиент вызывает createCustomer() или findCustomerById(), будет вызван метод profile().
Листинг 2.27. CustomerService, использующий перехватчик ианнотацию обратного вызова
@Transactional
@Interceptors(ProfileInterceptor.class)
public class CustomerService {
··@Inject
··private EntityManager em;
··@PostConstruct
··public void init() {
····//…
··}
··public void createCustomer(Customer customer) {
····em.persist(customer);
··}
··public Customer findCustomerById(Long id) {
····return em.find(Customer.class, id);
··}
}
Связывание и исключение перехватчиков
Вы уже видели, как перехватываются вызовы в пределах одного компонента (с аннотацией @Around Invoke), а также среди множественных компонентов (с использованием аннотации @Interceptors). Спецификация Interceptors 1.2 также позволяет связать в цепочку несколько перехватчиков.
В действительности аннотация @Interceptors способна прикреплять более одного перехватчика, так как в качестве параметра она берет список перехватчиков, разделенных запятой. Когда определяются множественные перехватчики, порядок их вызова задается тем порядком, в котором они указаны в аннотации @Interceptors. Например, код в листинге 2.28 использует аннотацию @Interceptors у компонента и на уровне методов.
Листинг 2.28. CustomerService, соединяющий несколько перехватчиков
@Stateless
@Interceptors({I1.class, I2.class})
public class CustomerService {
··public void createCustomer(Customer customer) {…}
··@Interceptors({I3.class, I4.class})
··public Customer findCustomerById(Long id) {…}
··public void removeCustomer(Customer customer) {…}
··@ExcludeClassInterceptors
··public Customer updateCustomer(Customer customer) {…}
}
Когда клиент вызывает метод updateCustomer(), перехватчик не вызывается, так как метод аннотирован @ExcludeClassInterceptors. При вызове метода createCustomer() выполняется перехватчик I1, за которым следует перехватчик I2. При вызове метода findCustomerById() перехватчики I1, I2, I3 и I4 выполняются в соответствующем порядке.
Связывание с перехватчиком
Перехватчики определяются в своей собственной спецификации (запрос JSR 318) и могут использоваться в любых управляемых компонентах (EJB, сервлетах, веб-службах RESTful и т. д.). Но CDI расширил исходную спецификацию, добавив к ней связывание с перехватчиком. Это означает, что связывание с перехватчиком может применяться только тогда, когда активизирован CDI.
Если вы посмотрите на листинг 2.25, то увидите, как работают перехватчики. Реализацию перехватчика необходимо указывать непосредственно на реализации компонента (например, @Interceptors(LoggingInterceptror.class)). Это типобезопасно, но нет слабой связи. CDI обеспечивает связывание с перехватчиком, которое представляет определенный уровень косвенности и слабой связанности. Тип связывания с перехватчиком — это определенная пользователем аннотация, также сопровождаемая аннотацией @InterceptorBinding, которая связывает класс перехватчика с компонентом без прямой зависимости между этими двумя классами.
Листинг 2.29 показывает связывание с перехватчиком под названием Loggable. Как видите, данный код очень похож на квалификатор. Связывание с перехватчиком — это аннотация, также аннотированная @InterceptorBinding, которая может быть пустой или иметь члены (например, как в листинге 2.13).
Читать дальше