Wednesday, November 27, 2013

Spring AOP: A Go through with Simple Example

AOP concepts
I am assuming that you are aware of what is Aspect Oriented Programming and its use in applications. I will briefly describe some AOP concepts and terminology. These terms are not Spring-specific.
  • Aspect:  It is a general feature that you can apply to your application globally. It allows you to add common behaviour to your module without interfering in your business logic. (like logging, performance monitoring, exception handling, transaction management, etc).
  • Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
  • Advice: action taken by an aspect at a particular join point. Different types of advice include “around,” “before” and “after” advice. It is a piece of code that should be executed at specific aspect. Like you want to execute some logging before any method invocation aspect.
  •  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.
  • Target object: object being advised by one or more aspects. Also referred to as the advised object. Since Spring AOP is implemented using runtime proxies, this object will always be a proxied object.
  • AOP proxy: an object created by the AOP framework in order to implement the aspect methods (advice method executions).
Types of Advice:
  • Before advice:  Advice that executes before a join point.
  • After returning advice Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.
  • After throwing advice Advice to be executed if a method exits by throwing an exception.
Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans). Field interception is not implemented, although support for field interception could be added without breaking the core Spring AOP APIs. If you need to advise field access and update join points, consider a language such as AspectJ.
Spring And Advice:
We will start with advices. Advice implementations in Spring are simply implementations of the org.aopalliance.intercept.MethodInterceptor interface. But that’s not a Spring class. Spring’s AOP implementation uses a standard AOP API from the AOP Alliance (JAVA/J2EE AOP standards). The MethodInterceptor interface is actually a child of the org.aopalliance.intercept.Interceptor interface, which is a child of another interface, org.aopalliance.aop.Advice.
The MethodInterceptor interface is very simple:
1
2
3
public interface MethodInterceptor extends Interceptor {
        Object invoke(MethodInvocation invocation) throws Throwable;
}
Basically, when you write an advice for intercepting a method, you have to implement one method – the invoke method, and you are given a MethodInvocation object to work with. The MethodInvocation object gives different operations about the method that we’re intercepting, and also gives a hook to tell the method to go ahead and run.
Spring has multiple alternatives to the basic MethodInterceptor.They allow you to do more specific things related to advice. They include:
  • org.springframework.aop.MethodBeforeAdvice - Implementations of this interface have to implement this method:
1
void before(Method method, Object[] args, Object target) throws Throwable;
All you need to do is do what you need before the method is called (as the interface name implies).
  • org.springframework.aop.AfterReturningAdvice - This interface’s method will be called on the return from the invocation of a method. The method looks like this: 
1
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
You’ll notice it looks a whole like the before advice, but simply adds the Object’s return value to the method arguments.
  • org.springframework.aop.ThrowsAdvice - Instead of requiring you to implement a particular method, this is simply a ‘marker’ interface, and expects you to implement any number of methods that look like this:
1
void afterThrowing([Method], [args], [target], [some type of throwable] subclass)
Spring And PointCuts:
Point cut is 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. Pointcuts in Spring implement the org.springframework.aop.Pointcut interface and look like this:
1
2
3
4
public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
}
The method matcher simply describes which methods for a given class are considered valid joinpoints for this pointcut.
Spring has different implementation for static vs. dynamic method matching point cuts. Astatic method matcher pointcut knows at a runtime that which method to be considered as join point on that object. While a dynamic method matcher consulted at every method invocation (to determine which is valid joint point).
Dynamic method matcher includesorg.springframework.aop.support.JdkRegexpMethodPointcut and  org.springframework.aop.support.PerlRegexpMethodPointcut classes.
static method matcher, while having less flexibility (you can’t check the method invocation arguments), is, by design, much faster, as the check is only performed once, rather than at every method invocation. Static Matching pointcuts implementations look like this:
1
2
3
4
5
6
7
8
9
public class MyBeanPointcut extends StaticMethodMatcherPointcut {
@Override
    public boolean matches(Method method, Class arg1) {
        if (method.getName().toLowerCase().indexOf("aspect")!=-1) {
            return true;
        }
        return false;
    }
}
This requires implementing just one abstract method (although it’s possible to override other methods to customize behavior). It will be a joint point of any advice if executing method name contains string “aspect”.
Pointcut Advisor
Now we have to tie a point cut with advices. The most basic pointcut advisor is the org.springframework.aop.support.DefaultPointcutAdvisor class. We can do this by following definition in spring file.
1
2
3
4
5
6
7
8
9
<bean id="catchBeforeMethod" class="com.nik.aop.advice.CatchBeforeMethod" />
 
<bean id="pointcut" class="com.nik.aop.pointcut.MyBeanPointcut" />
 
<bean name="methodPointcut"
        class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice" ref="catchBeforeMethod" />
        <property name="pointcut" ref="pointcut" />
</bean>
Now, lets’ glue them all together and create very simple example of Spring AOP. I have a test bean which has a string attribute. We will change that attribute in an advice.
Spring AOP Proxy:
The basic way to create an AOP proxy in Spring is to use the ProxyFactoryBean. This gives complete control over point cuts and advice that will apply.
Following is my test proxy bean.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestBean {
 
    private String aspectName;
 
    public String getAspectName() {
        return aspectName;
    }
 
    public void setAspectName(String aspectName) {
        this.aspectName = aspectName;
    }
 
    @Override
    public String toString() {
        return "TestBean [aspectName=" + aspectName + "]";
    }
}
Define Advice:
1
2
3
4
5
6
7
8
9
10
public class CatchBeforeMethod implements MethodBeforeAdvice  {
 
    @Override
    public void before(Method method, Object[] arg1, Object target) throws Throwable {
        String aspectName = ((TestBean)target).getAspectName();
        System.out.println("Method Caught before execution: "+method.getName());
        System.out.println("Updating Bean in Method Before Advice.");
        ((TestBean)target).setAspectName("BeforeMethod");
    }
}
Define Pointcut.
1
2
3
4
5
6
7
8
9
10
public class MyBeanPointcut extends StaticMethodMatcherPointcut {
 
    @Override
    public boolean matches(Method method, Class arg1) {
        if (method.getName().toLowerCase().indexOf("aspect")!=-1) {
            return true;
        }
        return false;
    }
}
Spring file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    xsi:schemaLocation="http://www.springframework.org/schema/beans
 
 
    <bean id="aopBean" class="com.nik.aop.beans.TestBean">
        <property name="aspectName" value="No_Aspect" />
    </bean>
 
    <bean id="catchBeforeMethod" class="com.nik.aop.advice.CatchBeforeMethod" />
    <bean id="pointcut" class="com.nik.aop.pointcut.MyBeanPointcut" />
 
    <bean name="methodPointcut"
        class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice" ref="catchBeforeMethod" />
        <property name="pointcut" ref="pointcut" />
    </bean>
 
    <bean id="aopBeanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
 
        <property name="target" ref="aopBean" />
 
        <property name="interceptorNames">
            <list>
                <value>methodPointcut</value>
            </list>
        </property>
    </bean>
 
</beans>
Main Class:
1
2
3
4
5
6
7
8
9
10
11
12
public class MainClass {
 
    public static void main(String[] args) {
 
        ApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "/conf/spring_aop.xml" });
        TestBean aopBean = (TestBean) appContext.getBean("aopBeanProxy");
        String aspectName = aopBean.getAspectName();
        System.out.println("In Main Class **********************: ");
        System.out.println("    After MethodBefore Aspect: "+aspectName);
        System.out.println("Out Main Class **********************: ");
    }
}
Enjoy!

No comments: