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 parts of the code, or you can use the interceptors mechanism that has appeared in EJB 3.0.
If details and a small example of implementation are interesting I ask under kat.
')
Interceptors intercept calls to bins to perform certain actions in front of a method. A similar concept is similar to the servlet filter chain.
We will begin consideration 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){
The capture method must be annotated with @AroundInvoke, and later we will take a closer look at its properties.
Now, to intercept the call of the method we need, it is enough just to add an annotation to it with an indication of the required interceptor. For example:
public class Summ { @Interceptors (SimpleLogger.class) public double getResult(){
In this example, when someone calls the getResult method of the Summ class, before executing it, the addLog method of the SimpleLogger class first runs, and then it returns the getResult method.
To begin, consider the intercepting method.
It must have the @AroundInvoke annotation, and there can be only one method with this annotation in the intercept class.
This method may have a public, private protected or packet access modifier, but cannot be declared final or static.
The semantics of the annotated method should satisfy the following pattern:
Object <method name> (InvocationContext <variable name>) throws Exception.
After performing actions inside the intercepting method, it returns execution of the business method or another interceptor in the chain by calling context.proceed (). But, if you do not execute this call, no execution will be transferred to anyone, this can be useful, for example, when organizing an intersector with data validation, if validation failed, then you do not need to continue execution.
The InvocationContext interface allows us to access information about the method being called.
Consider his code.
public interface InvocationContext { public Object getTarget(); public Method getMethod(); public Object[ ] getParametrs(); public void setParametrs(Object[ ] ); public java.util.Map<String,Object> getContextData(); public Object proceed(); }
- getTarget returns the object in which the intercepted method was called
- getMethod returns the method of the bean from which the interceptor was called, but if the bean's life cycle was intercepted (for example, the @PreDestroy method), then 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 the execution
- getContextData returns a map that can be used by interceptors to interact with each other, that is, if one method intercepts with several interceptors, then 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 appropriate methods to the interceptor class. For example:
public class SimpleLogger{ @PostConstruct public void init(InvocationContext context){
Another special use of interceptors is that you can declare a so-called “default interceptor” whose action will be distributed to all classes within a single application. Such an interceptor can only be declared via the deployment descriptor, it looks like this
<interceptor-binding> <ejb-name>*<ejb-name> <interceptor-class> ru.interceptortest.Validate</interceptor-class> </interceptor-binding>
At the same time, if it is necessary, so that the action of this interceptor does not spread to any class, it must be marked with its annotations @ExcludeDefaultInterceptors.
Let's try to write a simple example using interseptor. First, create 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 by marking one of the methods with the @Interceptors annotation (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 getSummResult () method of this class, then in the server console, impleLogger.logAction will output:
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 class Count.
public double getMultiplyResult(){ return getFirstArgument() * getSecondArgument(); }
And additionally mark the entire class 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, you first work out the LifeLogger interceptor, and then SimpleLogger and in the server console you get:
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, to use 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
Anyone can get the source codes in the form of a small simple web application from the github (the project was done under 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