Annotated Advice Parameters

Overview

In generated advisor (default) mode, before, after and around advices can receive annotated parameters. The use of annotations provides maximum flexibility to the signature of advices, since an advice can receive only the values that are relevant to its execution. These parameters can be in any order and their type can be the type that is most suitable to the advice functionality, according to the joinpoints it intercepts.

Annotated Parameter Signature

The annotated parameter signature is defined as:

public <return-value> <any-method-name>(@<Annotation> <any type> arg0, @<Annotation> <any type> arg1, ... , @<Annotation> <any type> argN) throws <Exception1>,<Exception2>,...,<ExceptionN>

This signature is available for all kinds of advices in JBoss AOP (when running in the generated advisor instrumentation mode). Besides this one, JBoss AOP supports the default around signature, introduced in the Aspect example. The default signature can only be used to write around advices, and is supported on all instrumentation modes.

Basicaly, this signature allows an advice to receive zero or more annotated parameters, in any order, and with any valid type. Regarding the exception types, an advice is allowed to declare any exception in its signature. RuntimeExceptions are rethrown by JBoss AOP as is. The same goes for exceptions that are not runtime, if they are declared in the joinpoint exceptions list (supposing the joinpoint has an exception list). Otherwise, the exception will be wrapped in a RuntimeException. In all cases, the application would get the exception as if the joinpoint itself had thrown it.

A concrete example of this signature has been shown in the before/after example:

public void beforeAdvice(@JoinPoint Joinpoint joinPoint)

As we can see, this advice receives one parameter annotated with @JoinPoint.

Next, we will see all parameter annotations that can be used by an advice. Notice that an empty parameter list is also valid, as in the following signatures:

public void beforeNoParameters()

public Object aroundNoParameters() throws Throwable

public void afterNoParameters()

Parameter Annotations

JBoss AOP demands the use of parameter annotations to identify advice parameters. These parameters can represent several values, like the joinpoint target, a joinpoint argument, or its return value. In this example, we show all parameter annotations that can be used by before, after and around advices to identify those values. These annotations are located in the org.jboss.aop.advice.annotation package.

The type of an annotated parameter can be chosen accordingly to the value it refers to. For example, suppose you are intercepting a jointpoint with an argument of type ArrayList<String>. Advices that refer to this argument as being of type ArrayList<String>, or Collection<String> or Object would all be considered correct. This is useful because you can access specific operations and fields of the argument, without performing a cast, and yet you can receive a super type like Object and refer to arguments of different types when your advice is applied to different joinpoints.

As a convention, we named the advices of this example after the names of parameter annotations. Besides, all advices names are prefixed with their types names (before advice names start with "before", and so on). But it is important to point out that, as all previous examples, the name of advices can be chosen freely and JBoss AOP won't use these names to identify annotated parameters.

@JoinPoint

This annotation is used on objects that represent a joinpoint. If the advice is an around advice, the type of this object belongs to the Invocation class hierarchy. On all other advices, joinpoints are represented by types of the JoinPointBean interface hierarchy, as follows:

public void beforeJoinPoint(@JoinPoint JoinPointBean joinPoint)

public Object aroundJoinPoint(@JoinPoint CallerInvocation invocation) throws Throwable

public void afterJoinPoint(@JoinPoint ConstructorExecution joinPoint)

Notice how beforeJoinPoint advice receives a parameter of the supertype JoinPointBean while, in afterJoinPoint, the parameter is of the more specific type ConstructorExecution, valid only on constructor execution joinpoints. To see these and all other advice examples, open up the Aspect.java file.

@Target

This annotation is used on parameters that refer to the joinpoint target. The joinpoint target is defined according to the joinpoint type. For example, the target of a method execution is the object whose method is being executed. On a call joinpoint, the target is the object whose method is being called. On a field access joinpoint, the target is the object that contains the field. Notice that not all joinpoints have a target; namelly: static method executions and calls, static field reads and writes, and constructor executions and calls.

Examples of usage of this annotation follow:

public void beforeTarget(@Target Object target)

public Object aroundTarget(@Target POJO target) throws Throwable

public void afterTarget(@Target POJO target) throws Throwable

By opening up jboss-aop.xml, you can see that the target of all these advices is of type POJO, yet beforeTarget advice receives a target of type Object. Since POJO extends Object, this is perfectly valid, and useful when an advice intercepts joinpoints with different target types.

@Caller

This annotation is used on parameters that refer to the caller object, during a call joinpoint interception. This should be used only on call joinpoints. If the call is inside a static method, the caller will be null. Examples follow:

public void beforeCaller(@Caller POJO caller)

public Object aroundCaller(@Caller Driver target) throws Throwable

public void afterCaller(@Caller Object target)

@Return

Use this annotation only on after advices, to refer to the joinpoint return value:

public void afterFieldReturn(@Return Object joinPointReturn)

public void afterMethodReturn(@Return boolean joinPointReturn)

@Arg

This annotation is used to refer to a joinpoint argument. Since a joinpoint can receive more than one argument, this is the only annotation that can be used on more than one advice parameters. Look at these examples:

public void beforeConstructorArg(@Arg Object argument)

public Object aroundConstructorArg(@Arg String argument) throws Throwable

public void afterArg(@Arg int argument)

When the joinpoint receives multiple arguments, JBoss AOP infers to which argument an @Arg annotated parameter refers. In this example, both beforeConstructorArg and aroundConstructorArg are applied to POJO constructor, whose unique argument is of type java.lang.String. So, the parameters of these advices will refer to this constructor argument. On the other hand, afterArg advice is applied to more than one method executions:

public class POJO
{
   ...

   public boolean someMethod(int argument)
   {
      ...
   }

   public void method(long arg0, int arg1, long arg2, String arg3)
   {
      ...
   }

   ...
}

Wen intercepting POJO.someMethod(int) execution, the parameter of afterArg will contain the value of the single argument someMethod received, as it happens with beforeConstructorArg and aroundConstructorArg advices. But, when intercepting POJO.method(long,int,long,String), JBoss AOP will automatically associate afterArg parameter with the parameter at index position 1, of type int.

Associating the int value to to an argument of POJO.method(long,int,long,String) is easy, because there is only one joinpoint argument of that type. The same does not apply to the following advice:

public void beforeMethodArg(@Arg long argument)

Given that POJO->method(long,int,long,String) has two arguments of type long, JBoss AOP will pick the first long typed argument. If you want to refer to the second long typed argument, in position 2, you can use the optional @Arg.index element as is shown below:

public void beforeMethodArg2(@Arg(index=2) long argument)
This element can be set everytime JBoss AOP does not associate your @Arg annotated parameter with the joinpoint argument you want to receive.

@Args

Finally, this annotation is used on advice parameters of type java.lang.Object[], that contain the complete list of the joinpoint arguments.

Look at these examples:

public void beforeArgs(@Args Object[] arguments)

public Object aroundArgs(@Args Object[] arguments) throws Throwable

public void afterArgs(@Args Object[] arguments)

Use this annotation when you need a generic advice, that receives all arguments without knowing how many arguments there are, or their types. Using @Args instead of a list of @Arg annotated parameters is also useful when you need to change one or more joinpoint argument values. An example is the beforeArgs advice, that ovewrites two argument values of POJO.method(long,int,long,String) execution:

public void beforeArgs(@Args Object[] arguments)
{
   ...
   arguments[3] = "overridenString";
   arguments[1] = Integer.valueOf(((Integer)arguments[1]).intValue() - 50);
   ...
}

Avoid using @Args when none of those conditions apply. This parameter type can incur in the creation of an array and of wrapper objects (as is the case of argument 1 above). Besides, you have the extra cost of downcasting the arguments.

Needless to say, extreme care is needed when changing the arguments array. If an argument value is overriden with one of a different type, an error will be thrown.

Run the example

To compile and run (for further detail, refer to our Compiling and Running Examples Guide):

  $ ant run.aopc

It will javac the files and then run the AOPC precompiler to manipulate the bytecode, then finally run the example. The output should be similar to this:

_run.aopc:
     [java] Calling POJO constructor
     [java] ========================
     [java] >>> beforeConstructorArg: Object "Driver"
     [java] >>> aroundConstructorArg: String "Driver"
     [java] RUNNING new POJO("Driver")
     [java] >>> afterJoinPoint: ConstructorExecution Constructor[constructor=public POJO(java.lang.String)]

     [java] Setting POJO->field with "text" value
     [java] =======================================
     [java] >>> beforeJoinPoint: JoinPointBean Field Write[field=public java.lang.Object POJO.field]
     [java] >>> aroundTarget: POJO POJO@19209ea
     [java] >>> aroundArgs: arguments [text]

     [java] Reading POJO->field value
     [java] =========================
     [java] >>> aroundArgs: arguments null
     [java] >>> aroundNoParameters
     [java] >>> afterFieldReturn: Object text
     [java] >>> afterNoParameters

     [java] Calling POJO->method(int)
     [java] =========================
     [java] >>> aroundArgs: arguments [17]
     [java] RUNNING POJO->method(17)
     [java] >>> afterArg: int 17
     [java] >>> afterArgs: arguments [17]

     [java] Calling POJO->method(long, int, long, String)
     [java] =============================================
     [java] >>> beforeMethodArg: long 20L
     [java] >>> beforeMethodArg2: long 1000L
     [java] >>> beforeArgs changing arguments: from [20, 2, 1000, Driver]
     [java]                                    to [20, -48, 1000, overridenString]
     [java] >>> aroundArgs: arguments [20, -48, 1000, overridenString]
     [java] RUNNING POJO->method(20L, -48, 1000L, "overridenString")
     [java] >>> afterArg: int -48
     [java] >>> afterArgs: arguments [20, -48, 1000, overridenString]

     [java] Calling POJO->someMethod(int)
     [java] =============================
     [java] >>> beforeNoParameters
     [java] RUNNING POJO->someMethod()
     [java] >>> afterMethodReturn: boolean true
     [java] >>> afterArg: int 10

     [java] Calling POJO->callMethod()
     [java] ==========================
     [java] >>> aroundCaller: Driver null
     [java] >>> aroundArgs: arguments []
     [java] RUNNING POJO->callMethod()
     [java] >>> beforeTarget: Object POJO@19209ea
     [java] >>> beforeCaller: POJO POJO@19209ea
     [java] >>> aroundJoinPoint: CallerInvocation JoinPoint_MByM__N_6287195452448676113POJO_N_1153034853916900765_8@f99ff5
     [java] RUNNING POJO->calledMethod()
     [java] >>> afterTarget: POJO POJO@19209ea
     [java] >>> afterCaller: Object POJO@19209ea