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.
- @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. - @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 + “)'”);
} - @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.
- @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.
@Before
@Around
@After
@AfterReturning
@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.