Chapter 9. Interceptor bindings

Managed beans and EJB session and message-driven beans support interception. Interceptors are used to separate cross-cutting concerns from business logic. The Java Interceptors specification defines the basic programming model and semantics. This specification defines a typesafe mechanism for associating interceptors to beans using interceptor bindings.

Interceptor bindings may be used to associate interceptors with any managed bean that is not itself an interceptor or decorator or with any EJB session or message-driven bean. An interceptor instance is a dependent object of the object it intercepts.

9.1. Interceptor binding types

An interceptor binding type is a Java annotation defined as @Target({TYPE, METHOD}) or @Target(TYPE) and @Retention(RUNTIME).

An interceptor binding type may be declared by specifying the @javax.interceptor.InterceptorBinding meta-annotation.

@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Transactional {}

9.1.1. Interceptor binding types with additional interceptor bindings

An interceptor binding type may declare other interceptor bindings.

@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
@Transactional
public @interface DataAccess {}

Interceptor bindings are transitive—an interceptor binding declared by an interceptor binding type is inherited by all beans and other interceptor binding types that declare that interceptor binding type.

Interceptor binding types declared @Target(TYPE) may not be applied to interceptor binding types declared @Target({TYPE, METHOD}).

9.1.2. Interceptor bindings for stereotypes

Interceptor bindings may be applied to a stereotype by annotating the stereotype annotation:

@Transactional
@Secure
@RequestScoped
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action {}

An interceptor binding declared by a stereotype is inherited by any bean that declares that stereotype.

If a stereotype declares interceptor bindings, it must be defined as @Target(TYPE).

9.2. Declaring the interceptor bindings of an interceptor

The interceptor bindings of an interceptor are specified by annotating the interceptor class with the binding types and the @javax.interceptor.Interceptor annotation.

@Transactional @Interceptor
public class TransactionInterceptor {

   @AroundInvoke 
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }

}

An interceptor class may declare multiple interceptor bindings.

Multiple interceptors may declare the same interceptor bindings.

If an interceptor does not declare an @Interceptor annotation, it must be bound to beans using @Interceptors or ejb-jar.xml.

All interceptors declared using @Interceptor should specify at least one interceptor binding. If an interceptor declared using @Interceptor does not declare any interceptor binding, non-portable behavior results.

An interceptor for lifecycle callbacks may only declare interceptor binding types that are defined as @Target(TYPE). If an interceptor for lifecycle callbacks declares an interceptor binding type that is defined @Target({TYPE, METHOD}), the container automatically detects the problem and treats it as a definition error.

9.3. Binding an interceptor to a bean

An interceptor binding may be declared by annotating the bean class, or a method of the bean class, with the interceptor binding type.

In the following example, the TransactionInterceptor will be applied at the class level, and therefore applies to all business methods of the class:

@Transactional
public class ShoppingCart { ... }

In this example, the TransactionInterceptor will be applied at the method level:

public class ShoppingCart {

   @Transactional 
   public void placeOrder() { ... }

}

A bean class or method of a bean class may declare multiple interceptor bindings.

If the bean class of a managed bean declares or inherits a class level interceptor binding or a stereotype with interceptor bindings, it must not be declared final, or have any non-static, non-private, final methods. If a managed bean has a class-level interceptor binding and is declared final or has a non-static, non-private, final method, the container automatically detects the problem and treats it as a definition error.

If a non-static, non-private method of a bean class of a managed bean declares a method level interceptor binding, neither the method nor the bean class may be declared final. If a non-static, non-private, final method of a managed bean has a method level interceptor binding, the container automatically detects the problem and treats it as a definition error.

9.4. Interceptor enablement and ordering

By default, a bean archive has no enabled interceptors bound via interceptor bindings. An interceptor must be explicitly enabled by listing its class under the <interceptors> element of the beans.xml file of the bean archive.

<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
   <interceptors>
      <class>org.mycompany.myfwk.TransactionInterceptor</class>
      <class>org.mycompany.myfwk.LoggingInterceptor</class>
   </interceptors>
</beans>

The order of the interceptor declarations determines the interceptor ordering. Interceptors which occur earlier in the list are called first.

Each child <class> element must specify the name of an interceptor class. If there is no class with the specified name, or if the class with the specified name is not an interceptor class, the container automatically detects the problem and treats it as a deployment problem.

If the same class is listed twice under the <interceptors> element, the container automatically detects the problem and treats it as a deployment problem.

Interceptors declared using @Interceptors or in ejb-jar.xml are called before interceptors declared using interceptor bindings.

Interceptors are called before decorators.

An interceptor is said to be enabled if it is enabled in at least one bean archive.

9.5. Interceptor resolution

The process of matching interceptors to a certain lifecycle callback method, EJB timeout method or business method of a certain bean is called interceptor resolution.

For a lifecycle callback method, the interceptor bindings include the interceptor bindings declared or inherited by the bean at the class level, including, recursively, interceptor bindings declared as meta-annotations of other interceptor bindings and stereotypes.

For a business method or EJB timeout method, the interceptor bindings include the interceptor bindings declared or inherited by the bean at the class level, including, recursively, interceptor bindings declared as meta-annotations of other interceptor bindings and stereotypes, together with all interceptor bindings declared at the method level, including, recursively, interceptor bindings declared as meta-annotations of other interceptor bindings.

An interceptor is bound to a method if:

  • The method has all the interceptor bindings of the interceptor. A method has an interceptor binding of an interceptor if it has an interceptor binding with (a) the same type and (b) the same annotation member value for each member which is not annotated @javax.enterprise.util.Nonbinding.

  • The interceptor intercepts the given kind of lifecycle callback or business method.

  • The interceptor is enabled in the bean archive containing the bean.

For a custom implementation of the Interceptor interface defined in Section 11.1.2, “The Interceptor interface”, the container calls getInterceptorBindings() to determine the interceptor bindings of the interceptor and intercepts() to determine if the interceptor intercepts a given kind of lifecycle callback, EJB timeout or business method.

9.5.1. Interceptors with multiple bindings

An interceptor class may specify multiple interceptor bindings.

@Transactional @Secure @Interceptor
public class TransactionalSecurityInterceptor {

   @AroundInvoke 
   public void aroundInvoke() throws Exception { ... }

}

This interceptor will be bound to all methods of this bean:

@Transactional @Secure
public class ShoppingCart { ... }

The interceptor will also be bound to the placeOrder() method of this bean:

@Transactional
public class ShoppingCart {

   @Secure
   public void placeOrder() { ... }
 
}

However, it will not be bound to the placeOrder() method of this bean, since the @Secure interceptor binding does not appear:

@Transactional
public class ShoppingCart {

   public void placeOrder() { ... }
 
}

9.5.2. Interceptor binding types with members

Interceptor binding types may have annotation members.

@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Transactional {
   boolean requiresNew() default false;
}

Any interceptor with that interceptor binding type must select a member value:

@Transactional(requiresNew=true) @Interceptor
public class RequiresNewTransactionInterceptor {

   @AroundInvoke 
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }

}

The RequiresNewTransactionInterceptor applies to this bean:

@Transactional(requiresNew=true)
public class ShoppingCart { ... }

But not to this bean:

@Transactional
public class ShoppingCart { ... }

Annotation member values are compared using equals().

An annotation member may be excluded from consideration using the @Nonbinding annotation.

@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Transactional {
   @Nonbinding boolean requiresNew() default false;
}

Array-valued or annotation-valued members of an interceptor binding type should be annotated @Nonbinding in a portable application. If an array-valued or annotation-valued member of an interceptor binding type is not annotated @Nonbinding, non-portable behavior results.

If the set of interceptor bindings of a bean or interceptor, including bindings inherited from stereotypes and other interceptor bindings, has two instances of a certain interceptor binding type and the instances have different values of some annotation member, the container automatically detects the problem and treats it as a definition error.