The Java EE 7 Tutorial
54.2 Using Interceptors
To define an interceptor, use one of the interceptor metadata annotations listed in Table 54-1 within the target class, or in a separate interceptor class. The following code declares an @AroundTimeout
interceptor method within a target class:
@Stateless public class TimerBean { ... @Schedule(minute="*/1", hour="*") public void automaticTimerMethod() { ... } @AroundTimeout public void timeoutInterceptorMethod(InvocationContext ctx) { ... } ... }
If you are using interceptor classes, use the javax.interceptor.Interceptors
annotation to declare one or more interceptors at the class or method level of the target class. The following code declares interceptors at the class level:
@Stateless @Interceptors({PrimaryInterceptor.class, SecondaryInterceptor.class}) public class OrderBean { ... }
The following code declares a method-level interceptor class:
@Stateless public class OrderBean { ... @Interceptors(OrderInterceptor.class) public void placeOrder(Order order) { ... } ... }
54.2.1 Intercepting Method Invocations
Use the @AroundInvoke
annotation to designate interceptor methods for managed object methods. Only one around-invoke interceptor method per class is allowed. Around-invoke interceptor methods have the following form:
@AroundInvoke visibility Object method-name(InvocationContext) throws Exception { ... }
For example:
@AroundInvoke public void interceptOrder(InvocationContext ctx) { ... }
Around-invoke interceptor methods can have public, private, protected, or package-level access, and must not be declared static or final.
An around-invoke interceptor can call any component or resource that is callable by the target method on which it interposes, can have the same security and transaction context as the target method, and can run in the same Java virtual machine call stack as the target method.
Around-invoke interceptors can throw runtime exceptions and any exception allowed by the throws
clause of the target method. They may catch and suppress exceptions, and then recover by calling the InvocationContext.proceed
method.
54.2.1.1 Using Multiple Method Interceptors
Use the @Interceptors
annotation to declare multiple interceptors for a target method or class:
@Interceptors({PrimaryInterceptor.class, SecondaryInterceptor.class, LastInterceptor.class}) public void updateInfo(String info) { ... }
The order of the interceptors in the @Interceptors
annotation is the order in which the interceptors are invoked.
You can also define multiple interceptors in the deployment descriptor. The order of the interceptors in the deployment descriptor is the order in which the interceptors will be invoked:
... <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> ...
To explicitly pass control to the next interceptor in the chain, call the InvocationContext.proceed
method.
Data can be shared across interceptors.
-
The same
InvocationContext
instance is passed as an input parameter to each interceptor method in the interceptor chain for a particular target method. TheInvocationContext
instance'scontextData
property is used to pass data across interceptor methods. ThecontextData
property is ajava.util.Map<String, Object>
object. Data stored incontextData
is accessible to interceptor methods further down the interceptor chain. -
The data stored in
contextData
is not sharable across separate target class method invocations. That is, a differentInvocationContext
object is created for each invocation of the method in the target class.
54.2.1.2 Accessing Target Method Parameters from an Interceptor Class
You can use the InvocationContext
instance passed to each around-invoke method to access and modify the parameters of the target method. The parameters
property of InvocationContext
is an array of Object
instances that corresponds to the parameter order of the target method. For example, for the following target method, the parameters
property, in the InvocationContext
instance passed to the around-invoke interceptor method in PrimaryInterceptor
, is an Object
array containing two String
objects (firstName
and lastName
) and a Date
object (date
):
@Interceptors(PrimaryInterceptor.class) public void updateInfo(String firstName, String lastName, Date date) { ... }
You can access and modify the parameters by using the InvocationContext.getParameters
and InvocationContext.setParameters
methods, respectively.
54.2.2 Intercepting Lifecycle Callback Events
Interceptors for lifecycle callback events (around-construct, post-construct, and pre-destroy) may be defined in the target class or in interceptor classes. The javax.interceptor.AroundConstruct
annotation designates the method as an interceptor method that interposes on the invocation of the target class's constructor. The javax.annotation.PostConstruct
annotation is used to designate a method as a post-construct lifecycle event interceptor. The javax.annotation.PreDestroy
annotation is used to designate a method as a pre-destroy lifecycle event interceptor.
Lifecycle event interceptors defined within the target class have the following form:
void method-name() { ... }
For example:
@PostConstruct void initialize() { ... }
Lifecycle event interceptors defined in an interceptor class have the following form:
void method-name(InvocationContext) { ... }
For example:
@PreDestroy void cleanup(InvocationContext ctx) { ... }
Lifecycle interceptor methods can have public, private, protected, or package-level access, and must not be declared static or final. Lifecycle interceptors may throw runtime exceptions but cannot throw checked exceptions.
Lifecycle interceptor methods are called in an unspecified security and transaction context. That is, portable Java EE applications should not assume the lifecycle event interceptor method has access to a security or transaction context. Only one interceptor method for each lifecycle event (post-create and pre-destroy) is allowed per class.
54.2.2.1 Using AroundConstruct Interceptor Methods
@AroundConstruct
methods are interposed on the invocation of the target class's constructor. Methods decorated with @AroundConstruct
may only be defined within interceptor classes or superclasses of interceptor classes. You may not use @AroundConstruct
methods within the target class.
The @AroundConstruct
method is called after dependency injection has been completed for all interceptors associated with the target class. The target class is created and the target class's constructor injection is performed after all associated @AroundConstruct
methods have called the Invocation.proceed
method. At that point, dependency injection for the target class is completed, and then any @PostConstruct
callback methods are invoked.
@AroundConstruct
methods can access the constructed target instance after calling Invocation.proceed
by calling the InvocationContext.getTarget
method.
Caution: Calling methods on the target instance from an |
@AroundConstruct
methods must call Invocation.proceed
in order to create the target instance. If an @AroundConstruct
method does not call Invocation.proceed
, the target instance will not be created.
54.2.2.2 Using Multiple Lifecycle Callback Interceptors
You can define multiple lifecycle interceptors for a target class by specifying the interceptor classes in the @Interceptors
annotation:
@Interceptors({PrimaryInterceptor.class, SecondaryInterceptor.class, LastInterceptor.class}) @Stateless public class OrderBean { ... }
Data stored in the contextData
property of InvocationContext
is not sharable across different lifecycle events.
54.2.3 Intercepting Timeout Events
You can define interceptors for EJB timer service timeout methods by using the @AroundTimeout
annotation on methods in the target class or in an interceptor class. Only one @AroundTimeout
method per class is allowed.
Timeout interceptors have the following form:
Object method-name(InvocationContext) throws Exception { ... }
For example:
@AroundTimeout protected void timeoutInterceptorMethod(InvocationContext ctx) { ... }
Timeout interceptor methods can have public, private, protected, or package-level access, and must not be declared static or final.
Timeout interceptors can call any component or resource callable by the target timeout method, and are invoked in the same transaction and security context as the target method.
Timeout interceptors may access the timer object associated with the target timeout method through the InvocationContext
instance's getTimer
method.
54.2.3.1 Using Multiple Timeout Interceptors
You can define multiple timeout interceptors for a given target class by specifying the interceptor classes containing @AroundTimeout
interceptor methods in an @Interceptors
annotation at the class level.
If a target class specifies timeout interceptors in an interceptor class, and also has an @AroundTimeout
interceptor method within the target class itself, the timeout interceptors in the interceptor classes are called first, followed by the timeout interceptors defined in the target class. For example, in the following example, assume that both the PrimaryInterceptor
and SecondaryInterceptor
classes have timeout interceptor methods:
@Interceptors({PrimaryInterceptor.class, SecondaryInterceptor.class}) @Stateful public class OrderBean { ... @AroundTimeout private void last(InvocationContext ctx) { ... } ... }
The timeout interceptor in PrimaryInterceptor
will be called first, followed by the timeout interceptor in SecondaryInterceptor
, and finally the last
method defined in the target class.
54.2.4 Binding Interceptors to Components
Interceptor binding types are annotations that may be applied to components to associate them with a particular interceptor. Interceptor binding types are typically custom runtime annotation types that specify the interceptor target. Use the javax.interceptor.InterceptorBinding
annotation on the custom annotation definition and specify the target by using @Target
, setting one or more of TYPE
(class-level interceptors), METHOD
(method-level interceptors), CONSTRUCTOR
(around-construct interceptors), or any other valid target:
@InterceptorBinding @Target({TYPE, METHOD}) @Retention(RUNTIME) @Inherited pubic @interface Logged { ... }
Interceptor binding types may also be applied to other interceptor binding types:
@Logged @InterceptorBinding @Target({TYPE, METHOD}) @Retention(RUNTIME) @Inherited public @interface Secured { ... }
54.2.4.1 Declaring the Interceptor Bindings on an Interceptor Class
Annotate the interceptor class with the interceptor binding type and @Interceptor
to associate the interceptor binding with the interceptor class:
@Logged @Interceptor public class LoggingInterceptor { @AroundInvoke public Object logInvocation(InvocationContext ctx) throws Exception { ... } ... }
An interceptor class may declare multiple interceptor binding types, and more than one interceptor class may declare an interceptor binding type.
If the interceptor class intercepts lifecycle callbacks, it can only declare interceptor binding types with Target(TYPE)
, or in the case of @AroundConstruct
lifecycle callbacks, Target(CONSTRUCTOR)
.
54.2.4.2 Binding a Component to an Interceptor
Add the interceptor binding type annotation to the target component's class, method, or constructor. Interceptor binding types are applied using the same rules as @Interceptor
annotations:
@Logged public class Message { ... @Secured public void getConfidentialMessage() { ... } ... }
If the component has a class-level interceptor binding, it must not be final
or have any non-static
, non-private
final
methods. If a non-static
, non-private
method has an interceptor binding applied to it, it must not be final
, and the component class cannot be final
.
54.2.5 Ordering Interceptors
The order in which multiple interceptors are invoked is determined by the following rules.
-
Default interceptors are defined in a deployment descriptor, and are invoked first. They may specify the invocation order or override the order specified using annotations. Default interceptors are invoked in the order in which they are defined in the deployment descriptor.
-
The order in which the interceptor classes are listed in the
@Interceptors
annotation defines the order in which the interceptors are invoked. Any@Priority
settings for interceptors listed within an@Interceptors
annotation are ignored. -
If the interceptor class has superclasses, the interceptors defined on the superclasses are invoked first, starting with the most general superclass.
-
Interceptor classes may set the priority of the interceptor methods by setting a value within a
javax.annotation.Priority
annotation. -
After the interceptors defined within interceptor classes have been invoked, the target class's constructor, around-invoke, or around-timeout interceptors are invoked in the same order as the interceptors within the
@Interceptors
annotation. -
If the target class has superclasses, any interceptors defined on the superclasses are invoked first, starting with the most general superclass.
The @Priority
annotation requires an int
value as an element. The lower the number, the higher the priority of the associated interceptor.
Note: The invocation order of interceptors with the same priority value is implementation-specific. |
The javax.interceptor.Interceptor.Priority
class defines the priority constants listed in Table 54-2.
Table 54-2 Interceptor Priority Constants
Priority Constant | Value | Description |
---|---|---|
|
0 |
Interceptors defined by the Java EE Platform and intended to be invoked early in the invocation chain should use the range between |
|
1000 |
Interceptors defined by extension libraries that should be invoked early in the interceptor chain should use the range between |
|
2000 |
Interceptors defined by applications should use the range between |
|
3000 |
Low priority interceptors defined by extension libraries should use the range between |
|
4000 |
Low priority interceptors defined by the Java EE Platform should have values higher than |
Note: Negative priority values are reserved by the Interceptors specification for future use, and should not be used. |
The following code snippet shows how to use the priority constants in an application-defined interceptor:
@Interceptor @Priority(Interceptor.Priority.APPLICATION+200 public class MyInterceptor { ... }