Overview
JBoss AOP allows you to insert behavior between the caller of a method and the actual method being called. If you look at
Driver.java you will see that it is invoking a number of methods declared in
POJO.java. JBoss AOP allows you to intercept a method call and transparently insert behavior when the method is invoked.
What is an Interceptor?
Behavior that you want to insert when a method is executed must be encapsulated in a implementation of the
org.jboss.aop.advice.Interceptor interface. (
For those of you familiar with AOP terms, an Interceptor in JBoss is an aspect with only one advice.)
package org.jboss.aop.advice;
import org.jboss.aop.joinpoint.Invocation;
public interface Interceptor
{
public String getName();
public Object invoke(Invocation invocation) throws Throwable;
}
When an AOP'ed method is called, JBoss will break up the method into its parts: a java.lang.reflect.Method object and an
Object[]
array representing the arguments of the method. These parts are encapsulated in an
org.jboss.aop.joinpoint.Invocation object.
SimpleInterceptor.java is a simple implementation of an interceptor.
How do I apply an Interceptor to a method execution?
To bind an interceptor to a method execution, you must create an XML file. Open up
jboss-aop.xml and take a look. Let's first start by applying
SimpleInterceptor.java to the
POJO.noop() method.
<aop>
...
<bind pointcut="execution(public void POJO->noop())">
<interceptor class="SimpleInterceptor"/>
</bind>
...
</aop>
To apply the interceptor you must create a binding and a
pointcut that specifies where in your Java code you want the interceptor applied.
execution(method expression) defines
whenever the method noop() is executed. A method expression requires a
return type followed by a class expression followed by '->' followed by the method name followed by a list of parameters. You can optionally provide method attributes like 'public', 'static', etc. if so desired.
Method expressions
You do not have to specify the entire signature of the method and can use wildcards anywhere you want. The next binding defined in
jboss-aop.xml looks like this.
<bind pointcut="execution(* POJO->*(int))">
<interceptor class="MethodInterceptor"/>
</bind>
This binding says, whenever any POJO method that has one parameter that is an
int is executed, invoke the
MethodInterceptor. The next bindings shows another example.
<bind pointcut="execution(static * POJO->*(..))">
<interceptor class="MethodInterceptor"/>
</bind>
This binding says, whenever any static POJO method is executed invoke the
MethodInterceptor <bind pointcut="execution(* POJO$Bar->*(..))">
<interceptor class="MethodInterceptor"/>
</bind>
The above binding shows how to specify an inner class.
Decomposing the Interceptor class
When an intercepted method is executed, the AOP framework will call each bound interceptor in a chain within the same call stack. The Invocation object drives the chain. Interceptors call invocation.invokeNext() to proceed with the method invocation. After the chain is exhausted, Java reflection is called to execute the actual method. Because this is one call stack, you can place try/catch/finally blocks around invocation.invokeNext() to catch any exceptions thrown by the executed method if you so desired.
Each type of intercepted execution (method, constructor, field, etc.) has a specific class that extends the base class org.jboss.aop.joinpoint.Invocation. If you open up MethodInterceptor.java, you will see that you can typecast the Invocation parameter into a org.jboss.aop.joinpoint.MethodInvocation object. The MethodInvocation class allows you to obtain additional information about the particular method call like the java.lang.reflect.Method object representing the call, the arguments, and even the targetObject of the interception.
Configure with XML
If you want to do some XML configuration of the interceptor instance, you can have it implement org.jboss.util.xml.XmlLoadable.
public interface XmlLoadable
{
public void importXml(Element element);
}
Running
To compile and run:
$ ant
It will javac the files and then run the AOPC precompiler to manipulate the bytecode, then finally run the example. The output should read as follows:
run:
[java] --- pojo.noop(); ---
[java] <<< Entering SimpleInterceptor
[java] noop()
[java] >>> Leaving SimpleInterceptor
[java] --- pojo.test1(String param); ---
[java] test1(String param): hello world
[java] --- pojo.test1(int param); ---
[java] <<< Entering MethodInterceptor for: public void POJO.test2(int)
[java] test2(int param): 55
[java] >>> Leaving MethodInterceptor
[java] --- POJO.test2(); ---
[java] <<< Entering MethodInterceptor for: public static void POJO.test2()
[java] static method
[java] >>> Leaving MethodInterceptor