Spring AOP Aspect Precedence Example

0

When a pointcut expression results into multiple aspects, both will run at the same join point. The precedence of the aspects is undefined unless you have explicitly specified it. The precedence of aspects can be specified either by implementing the Ordered interface or by using the @Order annotation. In this article, we will see examples of both the methods.

Multiple Advices without precedence

In order to demonstrate the example, we will use the below test bean which has a method to concatenate two strings.

TestBean:

package com.javarticles;

public class TestBean {
    public String concatenate(String a, String b) {
        String result = a + b;
        System.out.println("Return " + result);
        return result;
    }
}

Our first aspect will do argument validation.

ArgumentValidation:

package com.javarticles;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.util.StringUtils;

@Aspect
public class ArgumentValidation {
    @Before(value = "execution(* concatenate(String, String)) and args(a, b)", argNames = "a,b")
    public void checkIfNull(String a, String b) {
        System.out.println("Do null check");
        if (StringUtils.isEmpty(a)) {
            throw new IllegalArgumentException("a is null");
        }
        if (StringUtils.isEmpty(b)) {
            throw new IllegalArgumentException("b is null");
        }        
    }       
}

Our second aspect will log the arguments and method call.

LogMethodCalls:

package com.javarticles;

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

@Aspect
public class LogMethodCalls {

    @Before(value = "execution(* *.*(..))")
    public void logMethodCall(JoinPoint joinPoint) {
        System.out.println("log method call");
        StringBuilder sb = new StringBuilder("Going to call ");
        sb.append(joinPoint.getSignature().getName());
        sb.append("(");      
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            sb.append(args[i]);
            if (i < args.length -1) {
                sb.append(",");
            }          
        }
        sb.append(")");
        System.out.println(sb.toString());
    }        
}

We must register the aspects in Spring container.

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="argumentValidation" class="com.javarticles.ArgumentValidation"/>
	<bean id="logMethodCalls" class="com.javarticles.LogMethodCalls"/>
	
</beans>

Let’s concatenate two string values.

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.concatenate('one', 'two')");
            testBean.concatenate("one", "two");    
        } finally {
            context.close();
        }
    }
}

As you can see first the null check happens and then we log the method call. We will see how we can reverse this order.

Output:

Call testBean.concatenate('one', 'two')
Do null check
log method call
Going to call concatenate(one,two)
Return onetwo

Order the advice using @Order

Given two pieces of before advice, the one with highest precedence runs first.
Given two pieces of after advice, the highest precedence advice runs last.
In this example, we will control the order of execution by annotating the aspect with the Order annotation. We just need to specify the precedence value in @Order. For example, @Order(0).

LogMethodCalls:

package com.javarticles;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;

@Aspect
@Order(0)
public class LogMethodCalls {

    @Before(value = "execution(* *.*(..))")
    public void logMethodCall(JoinPoint joinPoint) {
        System.out.println("log method call");
        StringBuilder sb = new StringBuilder("Going to call ");
        sb.append(joinPoint.getSignature().getName());
        sb.append("(");      
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            sb.append(args[i]);
            if (i < args.length -1) {
                sb.append(",");
            }          
        }
        sb.append(")");
        System.out.println(sb.toString());
    }
        
}

We will apply order to our second aspect by implementing the org.springframework.core.Ordered interface in the aspect class.
Given two aspects, the aspect returning the lower value from Ordered.getValue() has the higher precedence.

ArgumentValidation:

package com.javarticles;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;

@Aspect
public class ArgumentValidation implements Ordered {
    @Before(value = "execution(* concatenate(String, String)) and args(a, b)", argNames = "a,b")
    public void checkIfNull(String a, String b) {
        System.out.println("Do null check");
        if (StringUtils.isEmpty(a)) {
            throw new IllegalArgumentException("a is null");
        }
        if (StringUtils.isEmpty(b)) {
            throw new IllegalArgumentException("a is null");
        }        
    }

    public int getOrder() {
        return 1;
    }
        
}

If you don’t want to order the execution of advices, consider collapsing such advice methods into one advice method per join point in each aspect class.

Output:

Call testBean.concatenate('one', 'two')
log method call
Going to call concatenate(one,two)
Do null check
Return onetwo

Download the source code

This was an example about spring AOP Aspect precedence.

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

Comments are closed.