Thursday, June 17, 2010

AOP with Spring

One thing to note about Spring AOP is that it does runtime weaving of aspects compared to AspectJ's compile time weaving. The good thing is you can define your aspects as java classes without having to learn yet another expression language if you were to use AspectJ. Ok here i just put together a small application to show how easy it is to set up aspects within your Spring applcation.

First of all we will start with defining our service class;


public class ChildService {

public void advice(){
System.out.println("Ok!!!!");
}
}



This class simply defines one method called advice(). Our intention is to weave into the advice method of ChildService and advice the child before and after. Hence we will be using @Before and @After annotations within our Aspect to handle this. If you do remember if we were to do this without annotations we have to implement BeforeAdvice, AfterAdvice within our Aspect class. But with annotations its a mere fact of defining a class and annotating it.

Following is the Aspect which will weave into the advice() method of the ChildService class.


import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class AdviceChild {

@Pointcut("execution(* *.advice(..))")
public void adviceChild(){}

@Before("adviceChild()")
public void beforeAdvicing(){
System.out.println("Listen Up!!!!");
}

@After("adviceChild()")
public void afterAdvicing(){
System.out.println("You got that ?");
}


}


As you can see here i have first annotated the advice class with @Aspect. This tells the Spring container that this is an aspect. Then we denote out point cut to tell at what point we are to use this Aspect. The pointcut is defined to weave into methods that have the name advice with any parameters.

Then we simply define the @Before and @After annotations on our methods to say what needs to happen before and after the method execution. The value specified within these annotations is the name of the pointcut which is derived from the method name that we specify the @Pointcut annotation on in this case being adviceChild().

Ofcourse there is another annotation called @Around which we can use so that we do not need @Before or @After. A code snippet on how to use @Around is given below;






@Around("adviceChild()")
public void aroundAdvice(ProceedingJoinPoint jp){
System.out.println("Listen Up!!!!");
try {
jp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("You got that ?");
}


One point to note is that the method you annotate with @Around should have a ProceesingJoinPoint as a method parameter.


Lastly we need to tell the Spring container about our advice.We could either just wire the bean
named AnnotationAwareAspectJAutoProxyCreator which is a BeanPostProcessor
or we could simply use the custom configuration element provided by spring in the aop namepsace. Hence the configuration will be as follows;





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


<aop:aspectj-autoproxy />

<bean id="childServ" class="ChildService"/>

<bean id="chdAdviceAspect" class="AdviceChild"/>

</beans>


If you wanted to go with complete XML configuration without using annotations then your XML configuration will be as follows;




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




<bean id="childServ" class="ChildService" />

<bean id="chdAdviceAspect" class="AdviceChild" />

<aop:config>
<aop:aspect ref="chdAdviceAspect">
<aop:pointcut id="advice" expression="execution(* *.advice(..))" />
<aop:before method="beforeAdvicing" pointcut-ref="advice" />
<aop:after-returning method="afterAdvicing"
pointcut-ref="advice" />
</aop:aspect>
</aop:config>
</beans>


If your defining it with XML then you do not need to have the annotations within your
AdviceChild class. This approach is cleaner too because then you can use any plain
old class as an Aspect.


Note that if you were to define aspects the old fashioned way then you need to create a ProxyFactoryBean to represent your bean which you need to weave in your advice and inject your advice class as an interceptor name. This is very cumbersome as you need to define this to each and every bean you want to weave in your advice to. And hence this approach i have given here required very less XML and is not at cumbersome as the ProxyFactoryBean method.

And lastly i give you a test class for you to see a running configuration.


public class AspectTester {

/**
* @param args
*/
public static void main(String[] args) {

ApplicationContext appContext = new ClassPathXmlApplicationContext("appContext.xml");
ChildService chldService = (ChildService)appContext.getBean("childServ");
chldService.advice();
}

}



Thats it guys. If there are any queries do drop a comment.

Cheers!!!!