About

View Ryan Knight's profile on LinkedIn

 

Recent Posts

 

Twitter
Search
Monday
Sep052011

Using Annotations with Spring AOP

When I was first looking for information on how to use annotations with Spring AOP I skipped the section on @AspectJ support. Later I realized that this is the section on annotations. Spring AOP uses the same annotations as Aspect J. It is still a pure Spring implementation using dynamic proxies and does not use the AspectJ compiler and weaver.

As the documentation explains, “Spring 2.0 interprets the same annotations as AspectJ 5, using a library supplied by AspectJ for pointcut parsing and matching.” The reason for this is AspectJ already has a fairly extensive library for declaring pointcuts using annotations. For more information on @AspectJ see the website - http://www.eclipse.org/aspectj/

The example code for this tutorial is on github.

Spring Configuration

The first step is to configure spring to use the AspectJ library in the spring config file (i.e. spring-config.xml). You will also need to add the necessary schema definitions (see the example file here):

<aop:aspectj-autoproxy/>

And then tell Spring to scan your classes for the annotations:

<context:component-scan base-package="tmo">
	<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>

Creating an Annotation

The annotation is created using standard Java syntax. An important part is to declare the annotation to have a retention policy of runtime. The default value is class, which means the annotation is not retained by the JVM at runtime.

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Timed {

Declaring the Aspect

In this example (shown in the SystemTiming.java file) we will be declaring basic around advice, wrapping the annotated method. This example will record how long it took to a method to execute. The annotations used come from the AspectJ library. First we declare a class to be an aspect using @Aspect:

@Aspect
public class SystemTiming

Then in the class we declare the advice along with a pointcut expression used by Spring to know where to inject the advice:

@Around("@annotation( timedAnnotation )")
public Object processSystemRequest(final ProceedingJoinPoint pjp, Timed timedAnnotation) throws Throwable {

This says this around advice should be applied any methods marked with the Timed annotation. The pointcut expression actually matches the parameter declaration in the method. The best way to understand this is to think of the method actually being declared first and then the pointcut following. This becomes important as the pointcut expressions get more complicated. You can actually simplify the expression if you don’t need to access the annotation by leaving the annotation out of the method declaration.

@Around("@annotation( Timed )")
public Object processSystemRequest(final ProceedingJoinPoint pjp) throws Throwable {

Inside the advice we then need to call proceed to execute the actual method. An interesting benefit of this is we could stop execution of the method all together, for example if the user did not have the right credentials.

try {
	Object retVal = pjp.proceed();
	return retVal;
} catch (Throwable t) {
	throw t;
}

The advice can also get information about the method being wrapped. As an example we will add some basic pre and post processing around the method. For this example we want to wrap the method call in timing and then log the name of the method along with the time it took to execute the method:

long start = System.currentTimeMillis();
Object retVal = pjp.proceed();
long end = System.currentTimeMillis();
long differenceMs = end - start;

MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method targetMethod = methodSignature.getMethod();

System.out.println(targetMethod.getDeclaringClass().getName() + "." + targetMethod.getName() + " took " + differenceMs + " ms”);
return retVal;

Using the Annotation to Log Method Calls

Using the annotation is very simple. All that is needed is to add the annotation above a method (shown in the RateServiceImpl.java file):

@Timed
public void doWork() { ..... }

By simply adding this annotation to the method now the around advice will be applied and the method timing will be logged.

Advanced Spring AOP with Annotations - Adding Values to an Annotation

Annotations can have variable declarations that allow you to pass extra information into the advice:

public @interface Timed {
	String timingNotes;

To access the annotation, the annotation needs to be passed as a parameter to the advice:

@Around("@annotation( timedAnnotation )")
public Object processSystemRequest(final ProceedingJoinPoint pjp, Timed timedAnnotation) throws Throwable {

Then inside the advice we can access the annotation information:

String timingNotes = timedAnnotation.timingNotes();
//log additional timing notes here ....

When the annotation is declared, then additional information can be added to the annotation.

@Timed(timingNotes = "this is a slow service")
public void doWork() { ..... }

This example would allow you to put additional notes or information that needs to be logged along with the method timing.

Accessing method arguments

As a final addition to the advice suppose all the methods we are logging have a common parameter that we want to access. For example suppose each method takes an authentication token as a parameter and we want to log that token (probably not a good idea because this would create a security hole in the system).

   @Around(value = "@annotation( timedAnnotation )  && args(param)")
    public Object processSystemRequest(final ProceedingJoinPoint pjp, Timed timedAnnotation, final ServiceRequest param) throws Throwable {
 

Inside the method we can now access the parameter to find out request information:

String requestInfo = param.getRequestInfo();

That is all it takes to use annotations with Spring AOP. Let me know if you have any questions!

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>
« Tutorial: Play Framework 2.0 Beta with Ebean, JSON & jQuery | Main | Protection of Intellectual Property in Romania »