Java Interceptors (in EJB 3.0)

    Imagine that you have already written a considerable part of the application code, and it turns out that you need to add logging to the call of a large number of methods, or you need to add additional validation of input data to some similar methods.
    You can simply rewrite the necessary sections of code, or you can use the interceptors mechanism that appeared in EJB 3.0.

    If details are interesting and a small example of implementation, I ask for cat.



    Intceptors intercept calls to bin methods to perform certain actions before the method. A similar concept is similar to filter chains in servlets.

    We begin our discussion with a simple example.
    To use the interceptor, we first define the class itself, with a method that will intercept calls. Let it be a simple logger.

    public class SimpleLogger{
       @AroundInvoke
        public Object addLog(InvocationContext context){
            //какая-то логика логирования
        return context.proceed();
    }
    

    The intercepting method should be marked with the @AroundInvoke annotation, later we will examine its properties in more detail.

    Now, to intercept the call to the method we need, just add an annotation to it indicating the desired interceptor. For example, like this:

    public class Summ {
        @Interceptors (SimpleLogger.class)
         public double getResult(){
               //логика 
         }
    }
    

    In this example, when someone calls the getResult method of the Summ class, before it is executed, the addLog method of the SimpleLogger class is first executed, after which it returns execution to the getResult method.

    First, consider the intercept method.
    It must have the @AroundInvoke annotation, and there can only be one method with this annotation in the intercept class.
    This method can have a public, private protected, or packet access modifier, but cannot be declared final or static. The
    semantics of the annotated method must satisfy the following pattern:
    Object <method name> (InvocationContext <variable name>) throws Exception.

    After performing actions inside the intercepting method, it returns execution to the business method or another interceptor in the chain by calling context.proceed (). But, if you do not complete this call, then no one will be given the execution, it can be useful, for example, when organizing an interceptor with data validation, if validation fails, then you do not need to continue the execution.

    The InvocationContext interface allows us to access information about the called method.
    Consider its code.

    public interface InvocationContext {
         public Object getTarget(); 
         public Method getMethod(); 
         public Object[ ] getParametrs();
         public void setParametrs(Object[ ] );
         public java.util.Map getContextData(); 
         public Object proceed();
    }
    

    • getTarget returns the object in which the intercepted method was called
    • getMethod returns the method of the bean for which the interceptor was called, but if the bean's life cycle was intercepted (for example, the @PreDestroy method), it will return null
    • getParametrs will return the input parameters of the method as an array of objects
    • setParametrs allows you to change the method parameters during execution
    • getContextData returns a map that can be used by interceptors to interact with each other, that is, if one method is intercepted by several interceptors, the first can write some value to this map, and subsequent interceptors can read these values ​​for their own purposes.


    You can also intercept callback methods of the life cycle of the intercepted bean, for this you need to add the corresponding methods to the interceptor class. For example, like this:

    public class SimpleLogger{
        @PostConstruct
        public void init(InvocationContext context){
             //логирование создания объекта, например через context.getTarget() 
             context.proceed():
        }
        @PreDestroy
        public void remove(InvocationContext context){
             //какая-то логика
        context.proceed():
        }
    }
    

    Another special use of interceptors is that you can declare the so-called “default interceptor” whose action will be extended to all classes within the same application. Such an interceptor can only be declared through the deployment descriptor, it looks like this

    * ru.interceptortest.Validate

    Moreover, if it is necessary that the action of this interceptor does not extend to any class, it must be marked with its annotations @ExcludeDefaultInterceptors.

    Let's try to write a simple example using an interceptor. First, create the interceptor classes.
    Let's make the simplest class that displays messages in the server console when the intercepted method is called.

    public class SimpleLogger {
        @AroundInvoke
        public Object logAction(InvocationContext context) throws Exception {
            System.out.println("object  - " + context.getTarget().getClass()); 
            System.out.println( "method - " + context.getMethod());      
            return context.proceed();
        }
    }
    

    And create a simple class that performs arithmetic operations, marking one of the methods with the annotation @Interceptors (SimpleLogger.class).

    @Stateless
    public class Count implements Serializable{
        double firstArgument=0;
        double secondArgument=0;
    //геттеры и сеттеры и тд    
        @Interceptors(SimpleLogger.class)
        public double getSummResult() {
            return getFirstArgument()+ getSecondArgument();
        }
    }
    

    If you call the method of this class getSummResult (), then impleLogger.logAction will output to the server console:

    object  - class ru.interceptorexample.Count
    method - public double ru.interceptorexample.Count.getSummResult()
    

    Add another interceptor class.

    public class LifeLogger {    
        @AroundInvoke
        public Object logAll(InvocationContext context) throws Exception {
            System.out.println("LogAll object  - " + context.getTarget());
            System.out.println("LogAll method - " + context.getMethod());       
            return context.proceed();
        }
        @PostConstruct
        public Object logCreate(InvocationContext context) throws Exception {
            System.out.println("create  - " + context.getTarget().getClass().getClass);     
            return context.proceed();
        }
    }
    

    And add another method to the Count class.

    public double  getMultiplyResult(){
             return getFirstArgument() * getSecondArgument();
    }
    

    And additionally mark the entire class with the annotation @Interceptors (LifeLogger.class).
    Now, when the getMultiplyResult method is called, first, when creating an instance of the Count class, the LifeLogger interceptor will output to the server console:

    create  - class ru.interceptorexample.Count
    

    and then:

    LogAll object  - class ru.interceptorexample.Count
    LogAll method - public double ru.interceptorexample.Count.getMultiplyResult()
    

    And if you call the getSummResult method, then the LifeLogger interceptor will work first, and then SimpleLogger will work in the server console:

    LogAll object  - class ru.interceptorexample.Count
    LogAll method - public double ru.interceptorexample.Count.getSummResult()
    object  - class ru.interceptorexample.Count
    method - public double ru.interceptorexample.Count.getSummResult()
    

    Obviously, for practical tasks, it would be advisable to replace the output from the console with normal logging, for example, using log4j, but I hope these examples were enough to understand how the interceptor mechanism works.

    Sources of information, for those who want to learn more about the work of this mechanism:
    1. manuals from oracle.com
    2. EJB 3 in Action - Debu Panda, Reza Rahman, Derek Lane ISBN: 1-933988-34-7

    Source codes in the form Those who wish can get a small simple web application from a github (they did the project for netbeans and glassfish, but I think it will not be difficult if you need to transfer it to another environment). github.com/tsegorah/JavaInterceptorsExample.git

    Also popular now: