
Using interceptors
Interceptors, as the name suggests, are methods that intercept other methods. With interceptors, you can write one method that will always run before one or other methods of your choice. Interceptors are useful when you are required to implement some cross-cutting concern, which should take kind of a global effect on some other scenarios. For example, suppose you want to log each method call in a payment processor bean, so you can later check what really happened during runtime in production. The direct mechanism to implement this is to write a logging function, then call it in each method in the payment processing bean. Although this seems simple, it's really redundant with the existence of interceptors. With interceptors, you can write your logging function once, and attach it with all other methods that you need to associate logging with. Interceptors also allow you to access the original method invocation context; this means that you can access parameters passed to the original method. Moreover, interceptors enable you to take control of the invocation itself, such as blocking the method invocation or even altering the return value.
Interceptors in CDI are a direct implementation to the aspect-oriented programming technique, you can also think of interceptors — recalling knowledge you may have with the servlets APIs, such as filters you map to servlets or JSPs. Filters intercept HTTP requests, and so do interceptors on CDI methods.
Common use cases of interceptors are:
- Logging
- Profiling
- Transaction management
- Authorization
Let's define our first CDI interceptor. Write a class called MyInterceptor with the following code:
public class MyInterceptor { @AroundInvoke public Object interceptMethod(InvocationContext ctx) throws Exception { Object retValue = ctx.proceed(); return "Intercepted! " + retValue; } }
interceptMethod(), annotated with @AroundInvoke, is the method that is going to intercept some other methods. @AroundInvoke tells the container that this method is an intercepting one, which will always run each time before some other methods get executed upon a user call.
In this intercepting method, you can do some pre and/or post operations, handle parameter's values, alter the return value, and even ban the original method call itself! The InvocationContext parameter is the object that you can use to retrieve the original method call information, and the most important part, proceeding with the original method call. The proceed() method in the InvocationContext object is used to
proceed the method call to the original one; note that if you did not call it, the intercepted method itself will not be called.
The next step is to associate this interceptor with some other methods. Recalling the MyPojo example, we will use the @Interceptors annotation to associate MyInterceptor with the getMessage method as follows:
@Dependent public class MyPojo { @Interceptors(MyInterceptor.class) public String getMessage() { return "Hello from MyPojo!"; }
By annotating the getMessage() method with the @Interceptors annotation, we tell the container that we wish to intercept the getMessage() method with the MyInterceptor interceptor. This means that whenever a call to getMessage() is performed, interceptMethod (annotated with @AroundInvoke in the MyIntercptor class) will be called.
Now, if we injected the MyPojo bean in our example servlet, and tried to print the getMessage() return value, we would get the following output:
Intercepted! Hello from MyPojo!
As you see, interceptMethod has called the original method, read its return value, altered it, and then returned it to us, prefixed with the string Intercepted!.
You can also apply an interceptor to a complete class, by annotating the class itself with the @Interceptors annotation as follows:
@Dependent @Interceptors(MyInterceptor.class) public class MyPojo { public String getMessage1() { return "MyPojo first message!"; } public String getMessage2() { return "MyPojo second message!"; } }
Now, all the methods in the MyPojo bean are intercepted using the MyInterceptor annotation. When trying to call any of them, the interceptMethod will intercept and produce the desired effect. Let's try to use MyPojo from our example servlet:
@WebServlet(urlPatterns = "/cdi-example") public class ExampleServlet extends HttpServlet { @Inject private MyPojo myPojo; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getOutputStream().println(myPojo.getMessage1()); resp.getOutputStream().println(myPojo.getMessage2()); } }
This will be the output, as expected:
Intercepted! MyPojo first message!
Intercepted! MyPojo second message!