Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Использование Interceptor-ов

Чтобы определить Interceptor, используйте одну из аннотаций метаданных Interceptor-а, перечисленных в таблице 57-1 внутри целевого класса или в отдельном классе Interceptor-а. Следующий код объявляет метод Interceptor-а @AroundTimeout в целевом классе:

@Stateless
public class TimerBean {
    ...
    @Schedule(minute="*/1", hour="*")
    public void automaticTimerMethod() { ... }

    @AroundTimeout
    public void timeoutInterceptorMethod(InvocationContext ctx) { ... }
    ...
}

Если вы используете классы Interceptor-ов, используйте аннотацию javax.interceptor.Interceptors, чтобы объявить один или несколько Interceptor-ов на уровне класса или метода целевого класса. Следующий код объявляет Interceptor-ы на уровне класса:

@Stateless
@Interceptors({PrimaryInterceptor.class, SecondaryInterceptor.class})
public class OrderBean { ... }

Следующий код объявляет класс Interceptor-а на уровне метода:

@Stateless
public class OrderBean {
    ...
    @Interceptors(OrderInterceptor.class)
    public void placeOrder(Order order) { ... }
    ...
}

Перехват вызовов методов

Используйте аннотацию @AroundInvoke для обозначения методов Interceptor-ов для методов управляемого объекта. Для класса разрешается только один метод Interceptor-а типа Around-invoke. Методы Interceptor-а типа Around-invoke имеют следующую форму:

@AroundInvoke
visibility Object method-name(InvocationContext) throws Exception { ... }

Например:

@AroundInvoke
public void interceptOrder(InvocationContext ctx) { ... }

Методы Interceptor-а типа Around-invoke могут иметь публичный, приватный, защищённый или пакетный доступ и не должны объявляться как статические или final.

Interceptor типа Around-invoke может вызывать любой компонент или ресурс, вызывающийся целевым методом, может иметь тот же контекст безопасности и транзакции, что и целевой метод, и может работать в том же стеке вызовов виртуальной машины Java, что и целевой метод.

Interceptor-ы типа Around-invoke могут генерировать исключения во время выполнения и любое исключение, разрешённое предложением throws целевого метода. Они могут перехватывать и подавлять исключения, а затем восстанавливаться, вызывая метод InvocationContext.proceed.

Использование нескольких Interceptor-ов с методом

Используйте аннотацию @Interceptors, чтобы объявить несколько Interceptor-ов для целевого метода или класса:

@Interceptors({PrimaryInterceptor.class, SecondaryInterceptor.class,
        LastInterceptor.class})
public void updateInfo(String info) { ... }

Порядок Interceptor-ов в аннотации @Interceptors определяет порядок вызова Interceptor-ов.

Вы также можете определить несколько Interceptor-ов в дескрипторе развёртывания. Порядок Interceptor-ов в дескрипторе развёртывания определяет порядок, в котором будут вызываться Interceptor-ы:

...
<interceptor-binding>
    <target-name>myapp.OrderBean</target-name>
    <interceptor-class>myapp.PrimaryInterceptor.class</interceptor-class>
    <interceptor-class>myapp.SecondaryInterceptor.class</interceptor-class>
    <interceptor-class>myapp.LastInterceptor.class</interceptor-class>
    <method-name>updateInfo</method-name>
</interceptor-binding>
...

Чтобы явно передать управление следующему Interceptor-у в цепочке, вызовите метод InvocationContext.proceed.

Данные могут быть разделены между Interceptor-ами.

  • Один и тот же объект InvocationContext передаётся в качестве входного параметра каждому методу Interceptor-а в цепочке Interceptor-ов для конкретного целевого метода. Свойство contextData объекта InvocationContext используется для передачи данных через методы Interceptor-а. Свойство contextData является объектом java.util.Map<String, Object>. Данные, хранящиеся в contextData, доступны для методов Interceptor-а далее по цепочке Interceptor-ов.

  • Данные, хранящиеся в contextData, не могут использоваться совместно для отдельных вызовов методов целевого класса. То есть для каждого вызова метода в целевом классе создаётся отдельный объект InvocationContext.

Доступ к параметрам целевого метода из класса Interceptor-а

Вы можете использовать объект InvocationContext, передаваемый каждому методу типа Around-invoke, для доступа и изменения параметров целевого метода. Свойство parameters в InvocationContext представляет собой массив объектов Object, который соответствует порядку параметров целевого метода. Например, для следующего целевого метода свойство parameters в объекте InvocationContext, передаваемом методу Interceptor-а типа Around-invoke в PrimaryInterceptor, имеет вид массива Object, содержащий два объекта String (firstName и lastName) и объект Date (date):

@Interceptors(PrimaryInterceptor.class)
public void updateInfo(String firstName, String lastName, Date date) { ... }

Вы можете получить доступ к параметрам и изменить их с помощью методов InvocationContext.getParameters и InvocationContext.setParameters соответственно.

Перехват событий Callback-вызова жизненного цикла

Interceptor-ы для событий Callback-вызова жизненного цикла (around-construct, post-construct и pre-destroy) могут быть определены в целевом классе или в классах Interceptor-ов. Аннотация javax.interceptor.AroundConstruct обозначает метод как метод Interceptor-а, который вставляется в вызов конструктора целевого класса. Аннотация javax.annotation.PostConstruct используется для обозначения метода в качестве Interceptor-а событий жизненного цикла post-construct. Аннотация javax.annotation.PreDestroy используется для обозначения метода в качестве Interceptor-а события жизненного цикла pre-destroy.

Interceptor-ы событий жизненного цикла, определённые в целевом классе, имеют следующую форму:

void method-name() { ... }

Например:

@PostConstruct
void initialize() { ... }

Interceptor-ы событий жизненного цикла, определённые в классе Interceptor-ов, имеют следующую форму:

void method-name(InvocationContext) { ... }

Например:

@PreDestroy
void cleanup(InvocationContext ctx) { ... }

Методы Interceptor-а жизненного цикла могут иметь публичный, приватный, защищённый или пакетный доступ и не должны объявляться как статические или final. Interceptor-ы жизненного цикла могут генерировать исключения во время выполнения, но не могут генерировать проверяемые исключения.

Методы Interceptor-а жизненного цикла вызываются в неопределённом контексте безопасности и транзакции. То есть переносимые приложения Java EE не должны предполагать, что метод Interceptor-а событий жизненного цикла имеет доступ к контексту безопасности или транзакции. Только один метод Interceptor-а для каждого события жизненного цикла (post-create и pre-destroy) разрешён для каждого класса.

Использование методов перехвата AroundConstruct

Методы@AroundConstruct вставляются в вызов конструктора целевого класса. Методы, аннотированные @AroundConstruct, могут быть определены только внутри классов Interceptor-ов или родительских классов Interceptor-ов. Нельзя использовать методы @AroundConstruct в целевом классе.

Метод @AroundConstruct вызывается после завершения инъецирования всех Interceptor-ов, связанных с целевым классом. Целевой класс создаётся, и инъецирование конструктора целевого класса выполняется после того, как все связанные методы @AroundConstruct вызвали метод Invocation.proceed. В этот момент инъецирование зависимостей для целевого класса завершается, и затем выполняются вызовы Callback-метода @PostConstruct.

Методы @AroundConstruct могут обращаться к созданному целевому объекту после вызова Invocation.proceed путём вызова метода InvocationContext.getTarget.

Внимание:

Вызов методов на целевом объекте из метода @AroundConstruct опасен, поскольку инъецирование на целевом объекте может быть не завершено.

Методы@AroundConstruct должны вызывать Invocation.proceed для создания целевого объекта. Если метод @AroundConstruct не вызывает Invocation.proceed, целевой объект не будет создан.

Использование нескольких Interceptor-ов Callback-методов жизненного цикла

Вы можете определить несколько Interceptor-ов жизненного цикла для целевого класса, указав классы Interceptor-ов в аннотации @Interceptors:

@Interceptors({PrimaryInterceptor.class, SecondaryInterceptor.class,
        LastInterceptor.class})
@Stateless
public class OrderBean { ... }

Данные, хранящиеся в свойстве contextData у InvocationContext, не доступны для разных событий жизненного цикла.

Перехват событий тайм-аута

Вы можете определить Interceptor-ы для методов тайм-аута сервиса таймера EJB, используя аннотацию @AroundTimeout для методов в целевом классе или в классе Interceptor-а. Разрешается только один метод @AroundTimeout на класс.

Interceptor-ы тайм-аута имеют следующую форму:

Object method-name(InvocationContext) throws Exception { ... }

Например:

@AroundTimeout
protected void timeoutInterceptorMethod(InvocationContext ctx) { ... }

Методы Interceptor-а тайм-аута могут иметь публичный, приватный, защищённый или пакетный доступ и не должны объявляться как статические или final.

Interceptor-ы тайм-аута могут вызывать любой компонент или ресурс, вызываемый целевым методом тайм-аута, и вызываются в той же транзакции и контексте безопасности, что и целевой метод.

Interceptor-ы тайм-аута могут обращаться к объекту таймера, связанному с целевым методом тайм-аута, с помощью метода getTimer объекта InvocationContext.

Использование нескольких Interceptor-ов тайм-аута

Вы можете определить несколько Interceptor-ов тайм-аута для данного целевого класса, указав классы Interceptor-ов, содержащие методы Interceptor-ов @AroundTimeout в аннотации @Interceptors на уровне класса.

Если целевой класс задаёт Interceptor-ы тайм-аута в классе Interceptor-а а также имеет метод Interceptor-а @AroundTimeout внутри самого целевого класса, Interceptor-ы тайм-аута в классах Interceptor-ов вызываются первыми, а затем определяются Interceptor-ы тайм-аута, определённые в целевом классе. Например, в следующем примере предположим, что классы PrimaryInterceptor и SecondaryInterceptor имеют методы Interceptor-а тайм-аута:

@Interceptors({PrimaryInterceptor.class, SecondaryInterceptor.class})
@Stateful
public class OrderBean {
    ...
    @AroundTimeout
    private void last(InvocationContext ctx) { ... }
    ...
}

Сначала будет вызван Interceptor тайм-аута в PrimaryInterceptor, затем Interceptor тайм-аута в SecondaryInterceptor и, наконец, метод last, определённый в целевом классе.

Связывание Interceptor-ов с компонентами

Типы привязки Interceptor-а — это аннотации, которые можно применять к компонентам, чтобы связать их с конкретным Interceptor-ом. Типы привязки Interceptor-а, как правило, представляют собой пользовательские типы аннотаций времени выполнения, которые определяют цель Interceptor-а. Используйте аннотацию javax.interceptor.InterceptorBinding в определении пользовательской аннотации и укажите цель с помощью @Target, задав один или несколько из TYPE ( Interceptor-ы уровня класса), METHOD (Interceptor-ы уровня метода), CONSTRUCTOR (Interceptor-ы вокруг конструктора) или любую другую допустимую цель:

@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Inherited
pubic @interface Logged { ... }

Типы связывания с Interceptor-ами могут также применяться к другим типам связывания с Interceptor-ами:

@Logged
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Inherited
public @interface Secured { ... }

Объявление привязок Interceptor-а на классе Interceptor-а

Аннотируйте класс Interceptor-а с типом привязки Interceptor-а и аннотацией @Interceptor, чтобы связать привязку Interceptor-а с классом Interceptor-а:

@Logged
@Interceptor
public class LoggingInterceptor {
    @AroundInvoke
    public Object logInvocation(InvocationContext ctx) throws Exception { ... }
    ...
}

Класс Interceptor-а может объявлять несколько типов привязки Interceptor-а, и более одного класса Interceptor-а может объявлять тип привязки Interceptor-а.

Если класс Interceptor-а перехватывает вызовы Callback-методов жизненного цикла, он может объявлять только типы привязки Interceptor-а с помощью Target(TYPE) или, в случае @AroundConstruct вызовов Callback-методов жизненного цикла, Target(CONSTRUCTOR).

Привязка компонента к Interceptor-у

Добавьте аннотацию типа привязки Interceptor-а к классу, методу или конструктору целевого компонента. Типы привязки Interceptor-ов применяются с использованием тех же правил, что и аннотации @Interceptor:

@Logged
public class Message {
    ...
    @Secured
    public void getConfidentialMessage() { ... }
    ...
}

Если компонент имеет привязку Interceptor-а на уровне класса, он не должен быть final или иметь какие-либо не-static, не-private final методы. Если к не-static, не-private методу применяется привязка Interceptor-а, он не должен быть final, а класс компонента не может быть final.

Упорядочение Interceptor-ов

Если Interceptor-ов несколько, порядок их вызова определяется следующими правилами.

  • Interceptor-ы по умолчанию определены в дескрипторе развёртывания и вызываются первыми. Они могут указывать порядок вызова или переопределять порядок, указанный с помощью аннотаций. Interceptor-ы по умолчанию вызываются в том порядке, в котором они определены в дескрипторе развёртывания.

  • Порядок перечисления классов Interceptor-ов в аннотации @Interceptors определяет порядок вызова этих Interceptor-ов. Любые настройки @Priority для Interceptor-ов, перечисленные в аннотации @Interceptors, игнорируются.

  • Если класс Interceptor-а имеет родительские классы, Interceptor-ы, определённые в родительских классах, вызываются первыми, начиная с самого общего родительского класса.

  • Классы Interceptor-ов могут устанавливать приоритет методов Interceptor-ов, устанавливая значение в аннотации javax.annotation.Priority.

  • После вызова Interceptor-ов, определённых в классах Interceptor-ов, конструктор целевого класса, Interceptor-ы типа Around-invoke или тайм-аута запускаются в том же порядке, что и Interceptor-ы в аннотации @Interceptors.

  • Если целевой класс имеет родительские классы, любые Interceptor-ы, определённые в родительских классах, вызываются первыми, начиная с самого общего родительского класса.

Аннотация @Priority требует указание значения типа int. Чем меньше число, тем выше приоритет соответствующего Interceptor-а.

Замечание:

Порядок вызова Interceptor-ов с одинаковым значением приоритета зависит от реализации.

Класс javax.interceptor.Interceptor.Priority определяет константы приоритета, перечисленные в таблице 57-2.

Таблица 57-2 Константы приоритета Interceptor-ов

Приоритет Константа

Значение

Описание

PLATFORM_BEFORE

0

Interceptor-ы, определённые платформой Java EE и предназначенные для раннего вызова в цепочке вызовов, должны использовать диапазон между PLATFORM_BEFORE и LIBRARY_BEFORE. Эти Interceptor-ы имеют самый высокий приоритет.

LIBRARY_BEFORE

1000

Interceptor-ы, определённые библиотеками расширений, которые должны вызываться на ранних этапах цепочки Interceptor-ов, должны использовать диапазон между LIBRARY_BEFORE и APPLICATION.

APPLICATION

2000

Interceptor-ы, определённые приложениями, должны использовать диапазон между APPLICATION и LIBRARY_AFTER.

LIBRARY_AFTER

3000

Interceptor-ы с низким приоритетом, определённые библиотеками расширений, должны использовать диапазон между LIBRARY_AFTER и PLATFORM_AFTER.

PLATFORM_AFTER

4000

Interceptor-ы с низким приоритетом, определённые платформой Java EE, должны иметь значения выше, чем PLATFORM_AFTER.

Примечание:

Отрицательные значения приоритета зарезервированы спецификацией Interceptor-ов для будущего использования и не должны использоваться.

В следующем фрагменте кода показано, как использовать константы приоритета в определяемом приложением Interceptor-е:

@Interceptor
@Priority(Interceptor.Priority.APPLICATION+200
public class MyInterceptor { ... }

Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)