Intercept method calls using CDI interceptors

Last Updated:  February 26, 2020 | Published: July 14, 2019

If you have cross-cutting concerns for several parts of your application you usually don't want to copy and paste the code. For Java EE applications the CDI (Context and Dependency Injection) spec contains the concept of interceptors which are defined in the Java Interceptor specification. With these CDI interceptors, you can intercept business method, timeouts for EJB timers, and lifecycle events.

With this blog post, I'll demonstrate where and how to use the interceptors for a Java EE 8 application, using Java 8 and running on Payara 5.192.

Injection points for interceptors

Even though interceptors are part of the CDI spec they can intercept: EJBs, session beans, message-driven beans, and CDI managed beans. The Java Interceptors 1.2 release (latest) is part of the maintenance release JSR-318 and the CDI spec builds upon its basic functionality.

The specification defines five types of injection points for interceptors:

  • @AroundInvoke intercept a basic method call
  • @AroundTimeout used to intercept timeout methods of EJB Timers
  • @AroundConstruct interceptor method that receives a callback after the target class is constructed
  • @PostConstruct intercept post-construct lifecycle events
  • @PreDestroy interceptor method for pre-destroy lifecycle events

For most of these injection points, I'll provide an example in the following sections.

Writing CDI interceptors

Writing a CDI interceptor is as simple as the following:

You just annotate a class with @Interceptor and add methods for intercepting your desired injection points. Within an interceptor method, you have access to the InvocationContext . With this object, you can retrieve the name of the method, its parameters and you can also manipulate them. Make sure to call the .proceed() method if you want to continue with the execution of the original method.

As an example I'm going to intercept the following EJB:

To demonstrate how to manipulate the method parameters, I'm going to change the amount of the .withdrawMoneyFromCustomer(String customer, BigDecimal amount) if the customer name is duke. In addition. I'm logging a single line to console once lifecycle event interceptors are triggered:

For a more realistic example, I'm creating an interceptor to intercept JAX-RS methods and check if a required HTTP header is set. If the header is not present, the server will return with an HTTP status 400:

The required HTTP header is stored in the annotation @SecurePayment(requiredHttpHeader="X-Duke"), which is used to bind an interceptor to a method/class, as you'll see in the next chapter.

Binding interceptors to methods and classes

Up until now we just created CDI interceptors but did not bind them to a specific method or class. For this, we'll use a custom annotation with @InterceptorBinding. The simplest annotation for this looks like the following:

We can also add custom attributes to the annotation, as we need it for your @SecurePayment binding to specify the required HTTP header:

Once this annotation is in place, we have to add it to the interceptor class and to the method or class (to include all methods of this class) we want to intercept:

Activating CDI interceptors

The CDI interceptors are not active by default and we have to activate them first. Currently, there are two possible ways to activate them:

  1. Add the fully qualified class name of the interceptor to the beans.xml file
  2. Add the @Priority(int priority) annotation to the interceptor

To show you how both work, I'm using the first approach for the first interceptor:

The second interceptor is activated using the annotation. With the priority number we can specify the execution order of interceptors if multiple apply for the same method:

Putting it all together and hitting /resources/payments/mike and /resources/payments/duke with the X-Secure-Payment header, results in the following log output:

For more information visit the Weld documentation (CDI reference implementation) or the website of the CDI spec.

You can find the source code for this example on GitHub.

Have fun using CDI interceptors,

Phil

>