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.