Monday, July 14, 2014

Easy Spring AOP

This article shows how to create aspects in Spring, using Spring AOP.

Quick notes:
  1. Spring AOP is not a fully blown AOP framework (read these few paragraphs). 
  2. It uses AspectJ annotations and pointcut expressions.
  3. Only join point is method execution.
  4. Since Aspects are actually Spring beans, you can inject stuff into it (I prefer using @Autowire for DI).
  5. Spring AOP is a proxy based implementation. This is the most common implementation outside of Spring. Spring will create a proxy for any bean that is being advised. This is called auto-proxying.

Personally always try to use 
  1. Spring's Classpath scanning (aka component scanning)

Ok. To create an aspect I need to:
  1. Create a Spring bean.
  2. Tell Spring that this bean is a Spring AOP aspect
  3. Define the pointcuts. I usually do this in the bean itself, but you can define the pointcuts in its own class.
  4. Define the advices. These will be methods in the bean.

Create a Spring bean
There are many ways to do it. I prefer "annotation-based configuration". So its just a simple annotation (and the xml config to enable annotation-based configuration. I include it here, but its beyond the scope of this document so go read up on it by yourself).

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <mvc:annotation-driven/>
    <context:component-scan base-package="net.gerardsetho.web"/>

</beans>


Here is my MyAnnotationClass. Note the @Component annotation
package net.gerardsetho.web;

import org.springframework.stereotype.Component;

@Component
public class MyAnnotationClass {
}


Tell Spring that this bean is a Spring AOP aspect
So Spring now thinks my class is a normal Spring singleton. We need to tell Spring that its also a Spring AOP aspect. There are 2 ways to do it. I prefer to use "@AspectJ aspects", which Spring recognises. This is done by adding a single line in the XML configuration:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation=".....">

    <mvc:annotation-driven/>
    <context:component-scan base-package="net.gerardsetho.web"/>


    <!--
    This tells Spring to look for AspectJ annotations in the source code, 
    and create aspects for them.
    It also tells Spring to create proxies for (other) beans which are 
    being advised.
    -->
    <aop:aspectj-autoproxy/>

</beans>


Define the pointcuts
I usually define the pointcuts in the Aspect class itself. I guess one could put them in another class, but I rarely needed to do so.

Defining pointcuts is done using the @Pointcut annotation (its an AspectJ annotation). Note that Spring AOP only understand method-invocation join points. So if you need to advise field-get or field-set join points, you need to use a full fledged AOP framework like Aspect J.

Moving on. I usually like to define a pointcut that matches every method in an advised class. 1 pointcut per class. I think it makes things clearer. Then I make another join point for the methods in the class. The code should make this clear.

@Component
@Aspect
public class MyAnnotationClass {


    /**
     * Pointcut that describes all join points (in Spring, 
     * the only join points are method invocations)
     * in the class / interface MyController.
     */
    @Pointcut( "target(net.gerardsetho.web.MyController)")
    private void MyControllerMethods() {}


    /**
     * any method called home,
     * with any number of arguments
     * which returns anything
     */
    @Pointcut( "execution(* home(..))")
    private void entrypoint() {}

}

Here are some examples of other types of pointcuts.


Define the advices
We can combine different pointcuts to define an advice. The below combines (logical AND) the above 2 pointcuts, and creates an advice to print to System.out. It uses @Before, but one can use any number of supported advice types.

    @Before( "MyControllerMethods() && entrypoint()" )
    public void logEntryPoints() {

        System.out.println( "logging entry point." );

    }

In addition to other types of advice, one can also access the parameters of the advised methods. Let me know in the comments if you're interested to know about that.



No comments:

Post a Comment