Wednesday, February 9, 2011

Spring AOP made simple

Certain types of code like logging, monitoring, security access checks, transaction boundary management etc apply to all methods in the application. These are known as cross cutting concerns. The poor programmers way of implementing cross cutting concerns is to the implement them in every method. Every method would begin with a checkAccess call, a fire call that fires a monitoring event, a call that starts a new transaction. When you implement a new method, you need to copy this common code to the new method.

Aspect oriented programming is a style and mechanism where common code factored out and is inserted before, after or around the real application code using either proxies or other techniques like code injection. Before we proceed any further, let us describe some AOP terminology.

An Aspect is a piece of common code that can be applied repeatedly to various places in the application. An example is code that may start a transaction or complete the transaction.

A join point is a point in the program, such as a method.

Advice is code that is part of the aspect and that is applied to a join point. Some different types of advice are before advice,after advice, around advice. Before advice is executed before the method. After advice is executed after the method is executed. In Around advice, part of the advice executes before the method and the other part after the method.

A pointcut is a set of join points to which advice may be applied. Generally some syntax or expression is used to match or determine the join points.

Enough threory. Let us get down to a real sample.Let us write a simple Spring application that prints a greeting. Then we will use Spring AOP to add a security check to the method by creating an Aspect with a before advice. Lastly we will add transaction support to the method by creating an Aspect with an around advice. Note that we will not be writing any real security or transaction code. The sample will just print a message where as in a real application you would have real code. You may download the complete source code at springaop.zip

For this tutorial you will also need
(1) Spring 3.0
(2) Eclipse is optional. I use eclipse as my IDE. But you can use other IDEs or command line tools as well. springaop.zip can be imported into eclipse.
(3) Some familiarity with the Spring framework.

Step 1: Code a simple Spring application
public interface Greeting {
    public String message(String target) ;
}

public class NewYearGreeting implements Greeting {
    public String message(String target) {
        System.out.println("From SpringAOP::" + target) ;
        return target ;
    }
    public static void main(String[] args) {
        ApplicationContext context =
        new ClassPathXmlApplicationContext("springaop.xml");
        Greeting sprg = (Greeting) context.getBean("greetbean");
        sprg.message("Happy new year 2011") ;
   }
} 
Add the bean definition in springaop.xml:
<bean id="greetbean" class="com.mj.spring.aop.NewYearGreeting"/>
Run the above application. The following line is output:
From SpringAOP::HappyNewYear

Step 2: Add @AspectJ support by adding the following line to springaop.xml. @Aspectj is a style where aspects are declared by adding annotations to java classes. This style was developed by the AspectJ project. We use these annotations in steps 3 and 4.
<aop:aspectj-autoproxy/>

Step 3:
Create a security Aspect as shown below. It is an ordinary java class with the annotation @Aspect. The checkAccess method is anotated as @Before and it is a before Advice. It applies to the pointcut execution(public * com.mj.spring.aop.*.*(..)). You can find more detailed explanation of this syntax in the Spring or AspectJ documentation.But you might have guessed that it means - execute the before advice before every public method of every class in the package com.mj.spring.aop.
@Aspect
public class SecurityAspect {
 @Before("execution(public * com.mj.spring.aop.*.*(..))")
 public boolean checkAccess() {
  System.out.println("Checking access before method invoke") ;
  return true ;
 }
}
Step 4: Create a transactions aspect as shown below. As in step 3, the class is annotated @Aspect.
@Aspect 
public class TransactionAspect {
 
 @Pointcut("execution(public * com.mj.spring.aop.*.*(..))")
 public void greetingmethod() {} 
 
 @Around("greetingmethod()") 
 public Object doTran(ProceedingJoinPoint pjp) throws Throwable {
  System.out.println("Starting a transaction Before calling the method") ;
  Object output = pjp.proceed();
  System.out.println("Completing the transaction After calling the method") ;
  return output ;
 }
}
The doTran method is the method that implements the around advice. The first parameter of the method that implements the around advice has to be of type ProceedingJoinPoint, which delegates the call to the actual target. Within the method you call the target by calling the proceed method. So the transaction is started before the proceed call which leads to target method invocation. The transaction is completed after the proceed() call - that is after the target method invocation. I have also made the pointcut generic so it can be applied to other methods if necessary. To define a pointcut that can be reused, create an empty method like greetingmethod() {} and add the @pointcut annotation to it. The @Around annotation on doTran uses this pointcut.

Step 5: Add the bean definitions for the aspects created in 3 & 4 to springaop.xml, so that the spring container knows about the aspects. This is an often forgotten step that can lead to unnecessary grief because the aspects will not get called.
<bean id="TransactionAspect" class="com.mj.spring.aop.TransactionAspect"/>
<bean id="SecurityAspect" class="com.mj.spring.aop.SecurityAspect"/>


Step 6: run the application. You will see the output:
Checking access before method invoke
Starting a transaction Before calling the method
in the method - From SpringAOP::Happy new year 2011
Commiting the transaction After calling the method


Using the techniques shown above, you can add sophisticated function to your application that is generally available only in complex containers like the EJB container or the SCA container. These containers typically use java dynamic proxies. SpringAOP is implemented using java dynamic proxies as well. But more importantly it makes it really easy to add cross cutting concerns to your application with need intrusive or repetetive code changes.