Spring AOP After Advice Annotation Example

0

You may want to log a warning if an SQL operation takes more time than it should. This kind of performance check on SQL statements needs to be applied at multiple points within the application but
it is something that should happen transparently rather than making explicit calls.

An SQL operation is something that is used in multiple points of an application, these functions that span multiple points of an application are called cross-cutting concerns.

We will use aspect-oriented programming (AOP) to separate the cross-cutting concerns. These cross-cutting concerns are modularized into special objects called aspects. In this article, we look into example of after advice using Spring AOP @After annotation.

After Advice

The action encapsulated within an aspect is called advice. Advice defines both the what and the when of an aspect. The ‘what’ part of the aspect is the action and ‘when’ part of the aspect is about when the advice to be invoked. Should it be applied before a method is invoked or after the method is invoked or should it be applied both before and after method invocation? Or should it only be applied if a method throws an exception?

The point in the execution of the application where an aspect can be plugged in is called Joinpoint. This point could be a method being called, an exception being thrown, or even a field being modified. An after advice is executed after a joinpoint finishes, whenever it returns a result or throws an exception abnormally.

We will use the below test bean containing methods with simple print statements. TestBean has overloaded methodA() and methodB(). We have overloaded methods so that it becomes clear how one can narrow down the join points using the point cut expressions.

Now what is Pointcut?
If advice defines the what and when of aspects then pointcuts define the where.

TestBean:

package com.javarticles;

public class TestBean {
    public void methodA() {
        System.out.println("'void methodA()' called");
    }
    
    public void methodA(String a, String b) {
        System.out.println("'void methodA(" + a + ", " + b + ")' called");
    }
    
    public String methodA(String a) {
        System.out.println("'String methodA(" + a + ")' called");
        return a;
    }
    
    public String methodB(String a, String b) {
        System.out.println("'String methodB(" + a +", " + b + ")' called");
        return a + b;
    }
}

AopBean is a simple bean annotated with AspectJ annotations. In this example, we will create a @After advice. Based on the pointcut expressions, spring figures out the methods on which it needs to apply the before advice. The expression is provided as annotation value.

Let’s go through each expression. The order of expressions is from specific to generic.

  1. @After(value = “execution(* methodA(String, String))”) – Applies on methods with signature void methodA(String, String). The preceding wildcard ‘*’ in this expression matches any modifier (public, protected, and private) and any return type.
  2. @After(value = “execution(* methodA(String, String)) and args(a, b)”, argNames = “a,b”) – Applies on methods with signature void methodA(String, String). The arguments of the method on which the advisor is applied are mapped to ‘a’ and b’ arguments of the advisor method.
    public void beforeMethodA3(String a, String b) {
    System.out.println(“@Before AOP, execution point: ‘void methodA(” + a + “, ” + b + “)'”);
    }
  3. @After(value = “execution(* methodA(..))”) – If you want the advisor to get applied on a method with any number of arguments then you need to use two dots in the argument list which means match any number of arguments.
  4. @After(value = “execution(* *.*(..))”) – You will ‘*’ in return value place as well as the method name which means it gets applied on any method.

AopBean:

package com.javarticles;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AopBean {
    @After(value = "execution(* methodA(String, String))")
    public void afterMethodA1() {
        System.out
                .println("@After AOP afterMethodA1(), execution point: '* methodA(String, String)'");
    }

    @After(value = "execution(* methodA(String, String)) and args(a, b)", argNames = "a,b")
    public void afterMethodA2(String a, String b) {
        System.out.println("@Before AOP afterMethodA2(), execution point: '* methodA(" + a
                + ", " + b + ")'");
    }
    
    @After(value = "execution(* methodA(..))")
    public void afterMethodA3() {
        System.out
                .println("@After AOP afterMethodA3(), execution point: '* methodA(..)'");
    }
    
    @After(value = "execution(* *.*(..))")
    public void afterAnyMethod() {
        System.out
                .println("@After AOP afterAnyMethod(), execution point: '* *.*(..)'");
    }    
}

In order for the Spring AOP to scan the aspectJ annotated beans, we need to include <aop:aspectj-autoproxy/>. It scans for beans annotated with @Aspect and then looks for Spring AOP Advisors for AspectJ methods annotated with any one of the below annotations.

  1. @Before
  2. @Around
  3. @After
  4. @AfterReturning
  5. @AfterThrowing

In the spring XML context, we define TestBean and AopBean.

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

 <bean id="testBean" class="com.javarticles.TestBean"/>
 <aop:aspectj-autoproxy/>
 <bean id="aopBean" class="com.javarticles.AopBean"/>
 
</beans>

Let’s now run the example.

SpringAopAfterAdviceExample:

package com.javarticles;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringAopAfterAdviceExample {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        try {
            TestBean testBean = (TestBean) context.getBean("testBean");
            System.out.println("Call testBean.methodA()");
            testBean.methodA();
            System.out.println("*************************************");
            System.out.println("Call testBean.methodA('one', 'two')");
            testBean.methodA("one", "two");
            System.out.println("*************************************");
            System.out.println("Call testBean.methodB('one', 'two')");
            testBean.methodB("one", "two");         
        } finally {
            context.close();
        }
    }
}

Output:

Call testBean.methodA()
'void methodA()' called
@After AOP afterAnyMethod(), execution point: '* *.*(..)'
@After AOP afterMethodA3(), execution point: '* methodA(..)'
*************************************
Call testBean.methodA('one', 'two')
'void methodA(one, two)' called
@After AOP afterAnyMethod(), execution point: '* *.*(..)'
@After AOP afterMethodA1(), execution point: '* methodA(String, String)'
@Before AOP afterMethodA2(), execution point: '* methodA(one, two)'
@After AOP afterMethodA3(), execution point: '* methodA(..)'
*************************************
Call testBean.methodB('one', 'two')
'String methodB(one, two)' called
@After AOP afterAnyMethod(), execution point: '* *.*(..)'

JoinPoint as method argument

In this section, we will see how to access the method arguments, its signature, the target bean etc using the Join Point. Suppose your advice needs more detailed information about the method being invoked to figure out the correct action then it access the access the current join point information by declaring an argument of type org.aspectj.lang.JoinPoint in the advice method signature.

We have added afterMethodA4(JoinPoint joinPoint) to AopBean to demonstrate the different fragments of information in JoinPoint object.

AopBean:

package com.javarticles;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AopBean {
    @After(value = "execution(* methodA(String, String))")
    public void afterMethodA1() {
        System.out
                .println("@After AOP afterMethodA1(), execution point: '* methodA(String, String)'");
    }

    @After(value = "execution(* methodA(String, String)) and args(a, b)", argNames = "a,b")
    public void afterMethodA2(String a, String b) {
        System.out.println("@Before AOP afterMethodA2(), execution point: '* methodA(" + a
                + ", " + b + ")'");
    }
    
    @After(value = "execution(* methodA(..))")
    public void afterMethodA3() {
        System.out
                .println("@After AOP afterMethodA3(), execution point: '* methodA(..)'");
    }

    @After(value = "execution(* *.*(..))")
    public void afterAnyMethod() {
        System.out
                .println("@After AOP afterAnyMethod(), execution point: '* *.*(..)'");
    }  
        
    @After(value = "execution(* methodA(..))")
    public void afterMethodA4(JoinPoint joinPoint) {
        System.out
                .println("@After AOP afterMethodA4(), execution point: '* methodA(..)'");
        System.out.println("Kind: " + joinPoint.getKind());
        System.out.println("args: " + Arrays.asList(joinPoint.getArgs()));
        System.out.println("Signature: " + joinPoint.getSignature());
        System.out.println("SourceLocation: " + joinPoint.getSourceLocation().getWithinType());      
        System.out.println("StaticPart: " + joinPoint.getStaticPart());
        System.out.println("Target: " + joinPoint.getTarget());
    }
        
}

Output:

Call testBean.methodA()
'void methodA()' called
@After AOP afterAnyMethod(), execution point: '* *.*(..)'
@After AOP afterMethodA3(), execution point: '* methodA(..)'
@After AOP afterMethodA4(), execution point: '* methodA(..)'
Kind: method-execution
args: []
Signature: void com.javarticles.TestBean.methodA()
SourceLocation: class com.javarticles.TestBean
StaticPart: execution(void com.javarticles.TestBean.methodA())
Target: [email protected]
*************************************
Call testBean.methodA('one', 'two')
'void methodA(one, two)' called
@After AOP afterAnyMethod(), execution point: '* *.*(..)'
@After AOP afterMethodA1(), execution point: '* methodA(String, String)'
@Before AOP afterMethodA2(), execution point: '* methodA(one, two)'
@After AOP afterMethodA3(), execution point: '* methodA(..)'
@After AOP afterMethodA4(), execution point: '* methodA(..)'
Kind: method-execution
args: [one, two]
Signature: void com.javarticles.TestBean.methodA(String,String)
SourceLocation: class com.javarticles.TestBean
StaticPart: execution(void com.javarticles.TestBean.methodA(String,String))
Target: [email protected]
*************************************
Call testBean.methodB('one', 'two')
'String methodB(one, two)' called
@After AOP afterAnyMethod(), execution point: '* *.*(..)'

Download the source code

This was an example about Spring AOP After Advise annotation.

You can download the source code here: springAopAfterAdviceAnnotationExample.zip
Share.

Comments are closed.