Spring AOP : Around Advice

Around advice is the most powerful kind of advice. It surrounds a join point such as a method invocation. round advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.

Lets see it by an exammple. The setup is similar like After advice post.  I have created a AroundAdviceModel whose method execution we will be monitoring by Aspects (AroundAdviceAspect.java). AroundAdviceMain.java is our test class where we actually load our spring configuration file and execute methds.

AroundAdviceModel.java


package mynotes.aop.model;

import java.util.Random;

public class AroundAdviceModel {

private String someString;

public String getSomeString() {
return someString;
}

public void setSomeString(String someString) {
System.out.println(someString);
this.someString = someString;
}


public int getRandomNumber(int upperLimit){
Random random=new Random();
int result=random.nextInt(upperLimit);
return result;
}

public int multiply(int x,int y){
return x*y;
}
}

AroundAdviceAspect.java


package mynotes.aop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AroundAdviceAspect {

@Around("args(String)")
public void logMessage1(ProceedingJoinPoint joinpoint){
try {
System.out.println("@Around Logging Aspect logMessage1() invoked before joinpoint.proceed()");
joinpoint.proceed();//this is where the joinpoint gets executed
System.out.println("@Around Logging Aspect logMessage1() invoked after joinpoint.proceed()");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("@Around Logging Aspect logMessage1() invoked at the end");
}

}

Above Around aspect will run for all method that takes in a String argument. It is mandatory to have a Around aspect method have ProceedingJoinPoint as method argument. ProceedingJoinPoint is actually the placeholder for the method. Calling  .proceed() on this actually executes the method that satisfied the pointcut expression. You can write your code around this .process() call, that is why its called around advice.

spring_aroundAdvice.xml


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<aop:aspectj-autoproxy />

<bean id="aroundLoggingAspect" class="mynotes.aop.aspect.AroundAdviceAspect" />

<bean id="aroundAdviceModel" class="mynotes.aop.model.AroundAdviceModel"/>

</beans>

AroundAdviceMain.java


package mynotes.aop.main;

import mynotes.aop.model.AroundAdviceModel;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AroundAdviceMain {

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring_aroundAdvice.xml");
AroundAdviceModel aAroundAdviceModel=context.getBean("aroundAdviceModel",AroundAdviceModel.class);
aAroundAdviceModel.setSomeString("value1");

}

}

Running above:


@Around Logging Aspect logMessage1() invoked before joinpoint.proceed()
value1
@Around Logging Aspect logMessage1() invoked after joinpoint.proceed()
@Around Logging Aspect logMessage1() invoked at the end

value1 is printed by the setter.

Lets test with the getRandomNumber() of the model. Adding the following aspect in AroundAdviceAspect.java


//wrong implementation
@Around("args(input)")
public void logMessage2(ProceedingJoinPoint joinpoint,int input){
try {
System.out.println("@Around Logging Aspect logMessage2() invoked before joinpoint.proceed()");
System.out.println("Input=>"+input);
joinpoint.proceed();//this is where the joinpoint gets executed
System.out.println("@Around Logging Aspect logMessage2() invoked after joinpoint.proceed()");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("@Around Logging Aspect logMessage2() invoked at the end");

}

Notice above that ProceedingJointPoint is the first argument. and an int second. The advice will run for all method that takes in int as an argument.

Now calling getRandomNumber() method from main(AroundAdviceMain.java) by passing say 100.
System.out.println(“Number returned finally =>”+aAroundAdviceModel.getRandomNumber(100));
Output:


@Around Logging Aspect logMessage2() invoked before joinpoint.proceed()
Input=>100
@Around Logging Aspect logMessage2() invoked after joinpoint.proceed()
@Around Logging Aspect logMessage2() invoked at the end
Exception in thread "main" org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public int mynotes.aop.model.AroundAdviceModel.getRandomNumber(int)

Advice ran but threw an exception. This is because this advice – logMessage2() returns a void but getRandomNumber() returned int. Changing the method.


@Around("args(input)")
public int logMessage2(ProceedingJoinPoint joinpoint,int input){
int returnValue=0;
try {
System.out.println("@Around Logging Aspect logMessage2() invoked before joinpoint.proceed()");
System.out.println("Input=>"+input);
returnValue=(int) joinpoint.proceed();//this is where the joinpoint gets executed
System.out.println("@Around Logging Aspect logMessage2() invoked after joinpoint.proceed()");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("@Around Logging Aspect logMessage2() invoked at the end");
return returnValue;
}

Output:


@Around Logging Aspect logMessage2() invoked before joinpoint.proceed()
Input=>100
@Around Logging Aspect logMessage2() invoked after joinpoint.proceed()
@Around Logging Aspect logMessage2() invoked at the end
Number returned finally =>98

As you can see, no exception came. Also, you can see that we can actully change the return type/value indirectly of the method itself, or say throw an exception.

ProceedingJoinPoint has many methods that could come in handy. For example if for getting parameters passed to the joinpoint, another way is as follows. Adding following in AroundAdviceAspect.java


@Around("args(int,int)")
public int logMessage3(ProceedingJoinPoint joinpoint){
int returnValue=0;
Object[] objectArray=joinpoint.getArgs();
for (int i = 0; i < objectArray.length; i++) {
System.out.println("i "+i+"=>"+objectArray[i]);
}
try {
System.out.println("@Around Logging Aspect logMessage3() invoked before joinpoint.proceed()");
returnValue=(int) joinpoint.proceed();//this is where the joinpoint gets executed
System.out.println("@Around Logging Aspect logMessage3() invoked after joinpoint.proceed()");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("@Around Logging Aspect logMessage3() invoked at the end");
return returnValue;
}

As you can see, above advice will only run for methods having 2 int arguments. The argumets passed has been now captured by using the getArgs() method of the ProceedingJoinPoint.

Running multiply() method from main.
System.out.println(“Result returned finally =>”+aAroundAdviceModel.multiply(12, 10));
Output:


i 0=>12
i 1=>10
@Around Logging Aspect logMessage3() invoked before joinpoint.proceed()
@Around Logging Aspect logMessage3() invoked after joinpoint.proceed()
@Around Logging Aspect logMessage3() invoked at the end
Result returned finally =>120

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: