Spring AOP : After Advice

After‘ advice is an Advice that executes after a join point. There are 3 kinds of After advice:

  • After returning advice: Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.
  • After throwing advice: Advice to be executed if a method exits by throwing an exception.
  • After (finally) advice: Advice to be executed regardless of the means by which a join point exits (normal or exceptional return).

Lets take each one of these by examples. I have created a AfterAdviceModel whose method execution we will be monitoring by Aspects (AfterAdviceAspect.java). AfterAdviceMain.java is our test class where we actually load our spring configuration file and execute methds.

AfterAdviceModel.java :


package mynotes.aop.model;

import java.util.Random;

public class AfterAdviceModel {

 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){
 if(upperLimit<=0){
 throw new RuntimeException("Value cannot be <=0");
 }
 Random random=new Random();
 int result=random.nextInt(upperLimit);
 return result;
 }
}

AfterAdviceAspect.java :


package mynotes.aop.aspect;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AfterAdviceAspect {

@After("args(String)")
 public void logMessage1(){
 System.out.println("@After Logging Aspect logMessage1() invoked");
 }
}

Notice the After advice here that will run for all methods which takes String as input.

spring_afterAdvice.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="afterLoggingAspect" class="mynotes.aop.aspect.AfterAdviceAspect" />

 <bean id="afterAdviceModel" class="mynotes.aop.model.AfterAdviceModel">
 <property name="someString" value="value1"></property>
 </bean>

</beans>

Note that in above bean creation we are setting the someString attribute value from spring, i.e by setter injection.

AfterAdviceMain.java :


package mynotes.aop.main;

import mynotes.aop.model.AfterAdviceModel;

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

public class AfterAdviceMain {

 public static void main(String[] args) {
 ApplicationContext context = new ClassPathXmlApplicationContext("spring_afterAdvice.xml");
 AfterAdviceModel aAfterAdviceModel=context.getBean("afterAdviceModel",AfterAdviceModel.class);
 aAfterAdviceModel.setSomeString("value2");
}

}

Output:


value1
value2
@After Logging Aspect logMessage1() invoked

As you can see, ‘value1’ is setted by spring and no aspect method is called, whereas value2 which is setted by us, aspect After advice ran, which means Aspect advice does not run of any DI run by the spring container during bean creation. 

Notice the getRandomNumber() method in AfterAdviceModel that takes in a int number and returns a random number. It will throw a RuntimeException is a negative number is passed. Lets write a AfterReturning advice for monitoring the method. Adding following in our AfterAdviceAspect.java :


@AfterReturning(pointcut="args(input)",returning="myReturn")
 public void logMessage2(int input,int myReturn){
 System.out.println("@AfterReturning Logging Aspect logMessage2() invoked");
 System.out.println("Input=>"+input);
 System.out.println("Return=>"+myReturn);
 }

Above advice will run only if the method completes normally i.e without throwing any exception. It has a pointcut expression and an returing attribute that will capture the value returned by the method (joinpoint) that satisfy pointcut expression. Here the pointcut expression will satisfy all the method that takes an int variable as argument. The value returned also int, is captured by the returning attribute. Running the method from or main method by passing say 100.

Output:


@AfterReturning Logging Aspect logMessage2() invoked
Input=>100
Return=>78

As you can see we captured both input and output of the method.

Lets capture the expcetion if a negative number is passed. Adding following Advice in AfterAdviceAspect.java.


@AfterThrowing(pointcut="args(input)",throwing="ex")
 public void logMessage3(int input,Exception ex){
 System.out.println("@AfterThrowing Logging Aspect logMessage3() invoked");
 System.out.println("Input=>"+input);
 System.out.println("Exception Thrown=>"+ex.getMessage());
 }

Above advice will run only when an exception is thrown. It has 2 attributes – pointcut expression and the ‘throwing’ attribute. Here we are capturing Exception in general, you can just catch RuntimeException also. Lets run the main method by passing say -100.

Output:


@AfterThrowing Logging Aspect logMessage3() invoked
Input=>-100
Exception Thrown=>Value cannot be <=0
Exception in thread "main" java.lang.RuntimeException: Value cannot be <=0
 at mynotes.aop.model.AfterAdviceModel.getRandomNumber(AfterAdviceModel.java:21)

Just @Aspect sometime is called finally as it will run regardless a method throws a exception or not. Adding following aspect in AfterAdviceAspect.java


 @After("args(int)")
 public void logMessage4(){
 System.out.println("@After Logging Aspect logMessage4() invoked");
 }

Running our main by passing 100. Output :


@After Logging Aspect logMessage4() invoked
@AfterReturning Logging Aspect logMessage2() invoked
Input=>100
Return=>88

As you can see that @After is ran first and then @AfterReturning.

This is because each Advice is given an order internally and while executing a joinpoint, the advice with lowest Order value gets executed first.

  • Before Advice – Order 1
  • Around Advice – Order 2
  • After Advice – Order 3
  • AfterReturning Advice – Order 4
  • AfterThrowing Advice – Order 5

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: