Chapter 11. Reflection and AOP

While AOP works fine for normal access to fields, methods and constructors, there are some problems with using the Reflection API for this using JBoss. The problems are:

11.1. Force interception via reflection

To address the issues with interceptors not being invoked when you use reflection, we have provided a reflection aspect. You bind it to a set of caller pointcuts, and it mounts the pre-defined interceptor/aspect chains. The jboss-aop.xml entries are:

    
   <aspect class="org.jboss.aop.reflection.ReflectionAspect" scope="PER_VM"/>

   <bind pointcut="call(* java.lang.Class->newInstance())">
      <advice name="interceptNewInstance" \
         aspect="org.jboss.aop.reflection.ReflectionAspect"/>
   </bind>

   <bind pointcut="call(* java.lang.reflect.Constructor->newInstance(java.lang.Object[]))">
      <advice name="interceptNewInstance" \
         aspect="org.jboss.aop.reflection.ReflectionAspect"/>
   </bind>

   <bind pointcut="call(* java.lang.reflect.Method->invoke(java.lang.Object, java.lang.Object[]))">
      <advice name="interceptMethodInvoke" \
         aspect="org.jboss.aop.reflection.ReflectionAspect"/>
   </bind>

   <bind pointcut="call(* java.lang.reflect.Field->get*(..))">
      <advice name="interceptFieldGet" \
         aspect="org.jboss.aop.reflection.ReflectionAspect"/>
   </bind>

   <bind pointcut="call(* java.lang.reflect.Field->set*(..))">
      <advice name="interceptFieldSet" \
         aspect="org.jboss.aop.reflection.ReflectionAspect"/>
   </bind>   

         

The ReflectionAspect class provides a few hooks for you to override from a subclass if you like. These methods described below.

   protected Object interceptConstructor(
		Invocation invocation,
		Constructor constructor,
		Object[] args)
		throws Throwable;
         

Calls to Class.newInstance() and Constructor.newInstance() end up here. The default behavior is to mount any constructor execution or caller interceptor chains. If you want to override the behaviour, the parameters are:

  • invocation - The invocation driving the chain of advices.
  • constructor - The constructor being called
  • args - the arguments being passed in to the constructor (in the case of Class.newInstance(), a zero-length array since it takes no parameters)

   protected Object interceptFieldRead(
      Invocation invocation,
      Field field,
      Object instance)
      throws Throwable;
         

Calls to Field.getXXX() end up here. The default behavior is to mount any field read interceptor chains. If you want to override the behaviour, the parameters are:

  • invocation - The invocation driving the chain of advices.
  • field - The field being read
  • instance - The instance from which we are reading a non-static field.

   protected Object interceptFieldWrite(
      Invocation invocation,
      Field field,
      Object instance,
      Object arg)
      throws Throwable;
         

Calls to Field.setXXX() end up here. The default behavior is to mount any field write interceptor chains. If you want to override the behaviour, the parameters are:

  • invocation - The invocation driving the chain of advices.
  • field - The field being written
  • instance - The instance on which we are writing a non-static field.
  • arg - The value we are setting the field to

   protected Object interceptMethod(
		Invocation invocation,
		Method method,
		Object instance,
		Object[] args)
		throws Throwable;
         

Calls to Method.invoke() end up here. The default behavior is to mount any method caller interceptor chains (method execution chains are handled correctly by default). If you want to override the behaviour, the parameters are:

  • invocation - The invocation driving the chain of advices.
  • method - The method being invoked
  • instance - The instance on which we are invoking a non-static method.
  • args - Values for the method arguments.

11.2. Clean results from reflection info methods

The ReflectionAspect also helps with getting rid of the JBoss AOP "plumbing" information. You bind it to a set of caller pointcuts, using the followingjboss-aop.xml entries :

   
   <bind pointcut="call(* java.lang.Class->getInterfaces())">
      <advice name="interceptGetInterfaces" \
         aspect="org.jboss.test.aop.reflection.ReflectionAspectTester"/>
   </bind>

   <bind pointcut="call(* java.lang.Class->getDeclaredMethods())">
      <advice name="interceptGetDeclaredMethods" \
         aspect="org.jboss.test.aop.reflection.ReflectionAspectTester"/>
   </bind>

   <bind pointcut="call(* java.lang.Class->getDeclaredMethod(..))">
      <advice name="interceptGetDeclaredMethod" \
         aspect="org.jboss.test.aop.reflection.ReflectionAspectTester"/>
   </bind>

   <bind pointcut="call(* java.lang.Class->getMethods())">
      <advice name="interceptGetMethods" \
         aspect="org.jboss.test.aop.reflection.ReflectionAspectTester"/>
   </bind>

   <bind pointcut="call(* java.lang.Class->getMethod(..))">
      <advice name="interceptGetMethod" \
         aspect="org.jboss.test.aop.reflection.ReflectionAspectTester"/>
   </bind>

   <bind pointcut="call(* java.lang.Class->getDeclaredFields())">
      <advice name="interceptGetDeclaredFields" \
         aspect="org.jboss.test.aop.reflection.ReflectionAspectTester"/>
   </bind>

   <bind pointcut="call(* java.lang.Class->getDeclaredClasses())">
      <advice name="interceptGetDeclaredClasses" \
         aspect="org.jboss.test.aop.reflection.ReflectionAspectTester"/>
   </bind>

   <bind pointcut="call(* java.lang.Class->getDeclaredField(..))">
      <advice name="interceptGetDeclaredField" \
         aspect="org.jboss.test.aop.reflection.ReflectionAspectTester"/>
   </bind>

         

This way the calls to Class.getMethods() etc. only return information that is present in the "raw" class, by filtering out the stuff added to the class by JBoss AOP.