Advice Return Types

Overview

Around and after advices can return values and overwrite the joinpoint value. In this example we show how to do that.

Default Signature

This signature, for around advices only, demands a java.lang.Object return type. Notice that this signature is valid for any joipoint, no matter whether it has a return value, and, if it does, whether this value is primitive or not (a wrapper object is expected if the joinpoint returns a primitive value).

An example of this signature is Aspect.aroundDefaultSignature() advice:

public Object aroundDefaultSignature(MethodInvocation invocation) throws Throwable

Open up jboss-aop.xml and verify that this advice is applied to a method that returns a primitive value, a method that returns a non-primitive value, and a void method.

Annotated-Parameter Signature

In this signature, return types are safely checked. Differently than the default signature, an advice can't return an invalid typed value. For example, by declaring to return java.lang.Object when the joinpoint return type is java.util.Collection, an advice could return a java.lang.String value (this would result in a RuntimeException). When using annotated-parameter signature, this code wouldn't compile. In that case, an advice could, however, return a java.util.List typed value, since this class is a subtype of the joinpoint return type.

Around Advices

When using an annotated-parameter signature, around advices are obliged to return a value. This is pretty much the same as with the default signature. However, here it must return a value with the same type as the joinpoint return value, or a subtype. Notice that, if the joinpoint return type is void, the around advice should be void too.

To illustrate all this, let's look at the around advices contained in this example, by starting with this one:

public int aroundAnnotatedParam(@JoinPoint MethodInvocation invocation) throws Throwable
{
   ...
   return  5000;
}

Aspect.aroundAnnotatedParam() advice overwrites the method return value with the 5000 int value. This advice intercepts a method that returns an int value, and has the same return type as it.

On the other hand, Aspect.aroundArrayList() does not return the same type of the joinpoint it intercepts. It returns a subtype instead:

public ArrayList aroundArrayList(@JoinPoint MethodInvocation invocation) throws Throwable
{
	...
}

This advice intercepts a method whose return type is java.util.List and, so, its return type is valid. It wouldn't be valid if it was a superclass of java.util.List, like java.util.Collection. This avoids the advice returning a java.util.Vector, for example, when the joinpoint return value is expected to be of type java.util.List.

Finally, take a look at aroundVoid:

public void aroundVoid(@JoinPoint MethodInvocation invocation) throws Throwable
{
   ...
}

This advice is void and can be applied only to joinpoints that don't return values. This is the case of void POJO.doNothing() method execution, and it would also be the case of a void method call, or a field read/write joinpoint.

After Advices

Differently from around advices, after advices are not forced to return a value. They should return it only when they intend to replace the joinpoint value.

Let's take a look at a few examples;

public int afterOverwriteReturnValue(@Return int returnValue)
{
	...
	return returnValue/5;
}

This advice is applied to a joinpoint that returns an int value. Notice that it receives the value returned by the joinpoint (or by an advice that has executed before this one, if there are other advices involved), and returns one fifith of its value.

Our next advice:

public List afterUnmodifiableList(@Return List returnValue)
{
	...
   return Collections.unmodifiableList(returnValue);
}

Intercepts POJO.getList() method, that returns a java.util.List object. The same return type is used by this advice, that overwrites the joinpoint return value with an unmodifiable version of the original list.

Since after advices are not enforced to return values, afterVoid advice, of type void, can be used to intercept any joinpoint, no matter wheter the joinpoint has a return value or not:

public void afterVoid()
{
   ...
}

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->getIntValue()
     [java] ===========================
     [java] >>> aroundDefaultSignature...
     [java] >>> aroundAnnotatedParam...
     [java] RUNNING POJO->getIntValue(): return 10
     [java] <<< aroundAnnotatedParam: returning 5000
     [java] <<< aroundDefaultSignature: returning invocation.invokeNext()
     [java] >>> afterOverwriteReturnValue: returning 5000/5
     [java] >>> afterVoid
     [java] RECEIVED type:int value:1000

     [java] Calling POJO->getList()
     [java] =======================
     [java] >>> aroundDefaultSignature...
     [java] RUNNING POJO->getList(): return [this, "element2"]
     [java] <<< aroundDefaultSignature: returning invocation.invokeNext()
     [java] >>> afterUnmodifiableList: returning list transformed as UnmodifiableList
     [java] >>> afterVoid
     [java] RECEIVED type:class java.util.Collections$UnmodifiableRandomAccessList value:[POJO@152544e, element2]

     [java] Calling POJO->doNothing()
     [java] =========================
     [java] >>> aroundDefaultSignature...
     [java] >>> aroundVoid...
     [java] RUNNING POJO->doNothing()
     [java] <<< aroundVoid
     [java] <<< aroundDefaultSignature: returning invocation.invokeNext()
     [java] >>> afterVoid
     [java] RECEIVED nothing