Reflection

There are two issues with using the Reflection API with JBoss AOP

  1. Aspects applied to constructors, fields and methods are bypassed
  2. JBoss AOP adds a small amount of extra fields and methods

As Java system classes in the java.lang.reflect package cannot be modified by JBoss AOP, the aspect org.jboss.aop.ReflectionAspect has been created to help with these issues. ReflectionAspect contains advices that can be bound with caller pointcuts where needed.

Aspects applied to constructors and fields are bypassed

The advice org.jboss.aop.ReflectionAspect will intercept your calls if you set up the caller pointcuts properly, and attach to any defined interceptor chains so that the same interceptions will occur as if accessing the field, or calling the method or constructor normally, i.e. without reflection. If you want to do something additional on interception, you can subclass it and override the methods: interceptConstructor(), interceptFieldRead(), interceptFieldWrite() and interceptMethod().

An example subclass of ReflectionAspect:

	package mypackage;

	import java.lang.reflect.Constructor;
	import java.lang.reflect.Field;

	import org.jboss.aop.joinpoint.Invocation;
	import org.jboss.aop.reflection.ReflectionAspect;

	public class MyReflectionAspect extends ReflectionAspect {
		protected Object interceptConstructor(
		Invocation invocation,
		Constructor constructor,
		Object[] args)
		throws Throwable {
			//Do your stuff
			return super.interceptConstructor(invocation, constructor, args);
		}

		protected Object interceptFieldRead(
		Invocation invocation,
		Field field,
		Object instance)
		throws Throwable {
			//Do your stuff
			return super.interceptFieldRead(invocation, field, instance);
		}

		protected Object interceptFieldWrite(
		Invocation invocation,
		Field field,
		Object instance,
		Object arg)
		throws Throwable {
			//Do your stuff
			return super.interceptFieldWrite(invocation, field, instance, arg);
		}

		protected Object interceptMethod(
		Invocation invocation,
		Method method,
		Object instance,
		Object[] args)
		throws Throwable {
			//Do your stuff
			return super.interceptFieldWrite(invocation, method, instance, arg);
		}
	}
As shown below, you will have information about what is being intercepted, so you could do your own filtering on what you do by configuring your aspect (by implementing XmlLoadable or using the javabean style configuration). The default behaviour of these methods is to mount the original interceptor chains.

Declare the aspect example:

  <aspect class="mypackage.MyReflectionAspect" scope="PER_VM"/>

Intercepting Class.newInstance()

Bind the interceptNewInstance advice to Class.newInstance() calls:

   <bind pointcut="call(* java.lang.Class->newInstance())">
       <advice name="interceptNewInstance" aspect="mypackage.MyReflectionAspect" />
   </bind>

Calls to Class.newInstance() will end up in MyReflectionAspect.interceptConstructor(), and the arguments are:

invocation - The invocation driving the chain of advices.

constructor - The constructor being called

args - the arguments being passed in to the constructor (in this case a zero-length array since Class.newInstance() takes no parameters)

Without the ReflectionAspect, interceptors bound to both constructor calls and execution will be ignored.

Constructor.newInstance()

Bind the interceptNewInstance advice to Constructor.newInstance() calls:

   <bind pointcut="call(* java.lang.reflect.Constructor->newInstance())">
       <advice name="interceptNewInstance" aspect="mypackage.MyReflectionAspect" />
   </bind>

Calls to Constructor.newInstance() will end up in MyReflectionAspect.interceptConstructor(), and the arguments are:

invocation - The invocation driving the chain of advices.

constructor - The constructor being called

args - the arguments being passed in to the constructor

Without the ReflectionAspect, interceptors bound to both constructor calls and execution will be ignored.

Intercepting Field.getXXX()

Bind the interceptFieldGet advice to Field.get*() calls:

   <bind pointcut="call(* java.lang.reflect.Field->get*(..))">
          <advice name="interceptFieldGet" aspect="mypackage.MyReflectionAspect" />
   </bind>

Calls to Field->getXXX() will end up in MyReflectionAspect.interceptFieldRead(), and the arguments are:

invocation - The invocation driving the chain of advices.

field - The field being read

instance - the instance on which we are reading a field

Without the ReflectionAspect, interceptors bound to field reads will be ignored.

Intercepting Field.setXXX()

Bind the interceptFieldGet advice to Field.set*() calls:

   <bind pointcut="call(* java.lang.reflect.Field->set*(..))">
       <advice name="interceptFieldSet" aspect="mypackage.MyReflectionAspect" />
   </bind>

Calls to Field->getXXX() will end up in MyReflectionAspect.interceptFieldWrite(), and the arguments are:

invocation - The invocation driving the chain of advices.

field - The field being written

instance - the instance on which we are writing a field

arg - the value we are setting the field to

Without the ReflectionAspect, interceptors bound to field writes will be ignored.

Intercepting Method.invoke()

Bind the interceptMethodInvoke advice to Field.set*() calls:

   <bind pointcut="call(* java.lang.reflect.Method->invoke(java.lang.Object, java.lang.Object[]))">
      <advice name="interceptMethodInvoke" aspect="mypackage.MyReflectionAspect"/>
   </bind>

Calls to Method->invoke() will end up in MyReflectionAspect.interceptMethod(), and the arguments are:

invocation - The invocation driving the chain of advices.

method - The method being invoked

instance - the instance on which we are invoking a method

args - the arguments being passed in to the method

Without the ReflectionAspect, interceptors bound to method calls will be ignored. Interceptors bound to method execution will however ALWAYS be invoked.

Cleaning fields, interfaces and methods added to a class by JBoss AOP

Interfaces

JBoss AOP adds the org.jboss.aop.Advised interface to advised classes. If you intend to be calling Class.getInterfaces() on an advised class, you should bind the interceptGetInterfaces advice to Class.getInterfaces() calls:

   <bind pointcut="call(* java.lang.Class->getInterfaces())">
       <advice name="interceptGetInterfaces" aspect="mypackage.MyReflectionAspect" />
   </bind>

Class.getInterfaces() will now return exactly the same as if the class had not been advised (i.e. the original class). This means org.jboss.aop.Advised is not in the list of interfaces returned.

Methods

JBoss AOP adds some methods, both public and private to advised classes.

Bind the interceptGetMethod advice to Class.getMethod() calls:

   <bind pointcut="call(* java.lang.Class->getMethod(..))">
       <advice name="interceptGetMethod" aspect="mypackage.MyReflectionAspect" />
   </bind>

Bind the interceptGetDeclaredMethod advice to Class.getMethod() calls:

   <bind pointcut="call(* java.lang.Class->getDeclaredMethod(..))">
       <advice name="interceptGetDeclaredMethod" aspect="mypackage.MyReflectionAspect" />
   </bind>

Both the bindings above intercept the calls so that if you try to get any of the methods in the unadvised class the method is returned as normal. If you try to get one of the methods added by JBoss AOP, a NoSuchMethodException is thrown.

The following advices remove the methods added by JBoss AOP and return the same methods as if the class was unadvised.

Bind the interceptGetDeclaredMethods advice to Class.getDeclaredMethods() calls:

   <bind pointcut="call(* java.lang.Class->getDeclaredMethods())">
       <advice name="interceptGetDeclaredMethods" aspect="mypackage.MyReflectionAspect" />
   </bind>

Bind the interceptGetMethods advice to Class.getDeclaredMethods() calls:

   <bind pointcut="call(* java.lang.Class->getMethods())">
       <advice name="interceptGetMethods" aspect="mypackage.MyReflectionAspect" />
   </bind>

Fields

JBoss AOP adds only private fields to advised classes, no public ones, so we only need to intercept the "declared" versions of the get field methods i.e. Class.getDeclaredFields() and Class.getDeclaredField().

Bind the interceptGetDeclaredFields advice to Class.getDeclaredFields() calls:

   <bind pointcut="call(* java.lang.Class->getDeclaredFields())">
       <advice name="interceptGetDeclaredFields" aspect="mypackage.MyReflectionAspect" />
   </bind>

Bind the interceptGetDeclaredField advice to Class.getDeclaredField() calls:

   <bind pointcut="call(* java.lang.Class->getDeclaredField(..))">
       <advice name="interceptGetDeclaredField" aspect="mypackage.MyReflectionAspect" />
   </bind>