Spring annotation based AOP and intercepting the ball

Spring AOP and Annotation based AspectJ

The concept

Recently I had to work on a requirement that asked that logs be kept when anyone performs certain actions upon the database.

For example if some chose to remove an item from the database – the action, who performed it, when it was performed and any extra information should be noted in the database.

Implementing this can be performed in a variety of ways. We could have simply told developers to call a function within any database methods to log the action. However Spring AOP provides a better way of doing this.

The idea

To make it easier to store the logs I wanted users to simply be able to annotate any methods they felt needed logging in the message.

Then using AspectJ we would have code that would intercept the methods and log the action in the database. All developers would need to do is very simply annotate their methods.

The Annotation

First of all I had to create the Java Method annotation that can be placed above the required methods.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAction {
	String actionPerformed();
}

The annotation simply has one parameter that allows the developer to specify the action performed such as ‘object deleted’ etc.

The aspect class

Next was to write the Spring aspect class which will perform the actual logging of the action

First of all we write a simple class and add the @Aspect annotation before the class

@Aspect
public class MyAspect

Next we need to define the Pointcut. As a Spring defintion a Pointcut is:

Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.

I’m writing a simple point cut that matches any public methods. This is coded like so:

@Pointcut(value="execution(public * *(..))")
public void anyPublicMethod() { }

The last part of the Aspect class is the Advice. Spring defines the advice as:

Advice: action taken by an aspect at a particular join point. Different types of advice include “around,” “before” and “after” advice. Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point.

I need my advice to handle the “around” advice and only methods with my @LogAction annotation

To do this I used the following method:

@Around("anyPublicMethod() && @annotation(logAction)")
public Object logAction(ProceedingJoinPoint pjp, LogAction logAction) throws Throwable {
}

The method uses my anyPublicMethod Pointcut and the @annotation(logAction) to match only public methods with the LogAction annotation specified.

I have also included the ProceedingJoinPoint parameter so I can access the various arguments in the annotated method.

The final Aspect class is as follows:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
 
@Aspect
public class MyAspect{
 
	@Pointcut(value="execution(public * *(..))")
	public void anyPublicMethod() {
	}
 
	@Around("anyPublicMethod() && @annotation(logAction)")
	public Object logAction(ProceedingJoinPoint pjp, LogAction logAction) throws Throwable {
 
		// Do what you want with the actionperformed
		String actionPerformed = logAction.actionPerformed();
 
		// Do what you want with the join point arguments
		for ( Object object : pjp.getArgs()) {
			System.out.println(object);
		}
 
		return pjp.proceed();
	}
}

Updating the Spring app config

To ensure that our @Aspect annotated class is picked up by Spring you simply need to add the following to your app-config.xml

<aop:aspectj-autoproxy/>
<bean id="myAspect" class="package.to.my.MyAspect" />

This assumes that you have the aop namespace within your app-config beans definition like so

<?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-2.5.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
 
.....
 
</beans>

Setting up intercepted methods

The final step is to mark the methods we want to be intercepted

This is as easy as doing the following

@LogAction(actionPerformed="doSomething")
public void doSomething(MyObject someParameter) {
}

Now whenever that method is executed we will firstly execute our Aspect code.

8 thoughts on “Spring annotation based AOP and intercepting the ball

  1. HI, good article.
    One question: what do you use ‘PointCut( execute** )’ why not did you use ‘PointCut(@target ) ‘ as you are only interested in method that have used this annotation of specific type??
    Regards,
    /Aamir

  2. Hi,

    i used your code sample but i don’t see the poincut executed ! it is just ignored!
    i am using aspectj 1.7 with Spring3

  3. To Expand a little on what Alex means. Spring AOP is proxy based and method calls to this.doSomething() will not be wrapped as you’d expect.

    Info from the Spring docs:

    The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.

    More info on this can be found here:

    http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies

  4. Or even more often situation. Class X with two methods methodA and methodB. Only methodB is annotated. If methodB is invoked from methodA, than logAction() will never be invoked.

  5. Hi.

    You forgot to mention that if two annotated methods are in one class(methodA and methodB) and methodA is invoking methodB than logAction(…) will be invoked only once…

  6. Make sure you are giving correct pacakge name for @pointcut. Here it is given such a way that , it will take all packages.

  7. Hi,

    i used your code sample but i don’t see the poincut executed ! it is just ignored!
    i am using aspectj 1.6.10 with Spring3

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>