Thursday, 27 June 2013

Intercept an @annotation with Aspetcj in Spring

Intercepting a method call marked with an @Annotation using AspectJ and Spring it's easy enough and it's a good approach in terms of flexibility, scalability and design.

  • It's flexible because in the case you want to intercept a different method, you just have to move your annotation somewhere else.
  • It's scalable because if you want to intercept more than one method, you just need to add the annotation in other methods.
  • It results also in a good and clean code.


First thing you need to do is include the dependencies. In case of maven the following will do it.
<!-- Dependencies for AspectJ and Spring AOP -->
                <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aop</artifactId>
                        <version>${spring.version}</version>
                </dependency>
 
                <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjrt</artifactId>
                        <version>1.6.11</version>
                </dependency>
 
                <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>1.6.11</version>
                </dependency>



Second, we need to create the annotation that we will use to mark the methods in the business logic.
package com.marco.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CommitTransaction {
}
 

Now, we need to create an interceptor. In this case (@After) it will be triggered when the method intercepted is finished.
For the full list of pointcuts and advises see the following pointcuts and advise
package com.marco.aspect;
import javax.inject.Inject;
import org.apache.log4j.Logger;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import com.marco.some.package.SomeClass;
import com.marco.some.package.SomeBean;
@Aspect
@Component
public class CommitTransactionInterceptor {

        private static final Logger LOGGER = Logger.getLogger(CommitTransactionInterceptor.class);

        @Inject
        private SomeBean someBean;

        @After("@annotation(com.marcot.CommitTransaction)")
        public void after() {

                LOGGER.debug("An invocation to " + SomeClass.class.getSimpleName() + " has been intercepted.");

                someBean.sendSomeEmail();

        }
}
 


Activate the aspectj and the interceptor in Spring adding the following in your application-context.xml
<?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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
                http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

        <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true" />

        <bean id="commitTransactionInterceptor" class="com.marco.aspect.CommitTransactionInterceptor"/>
</beans>


And of course, remember to load it in your spring configuration classes
@Configuration
@ImportResource({ "classpath:application-context.xml" })
public class YourSpringConfiguration {
....
}


All done, now you can mark all the methods you want to intercept with your annotation and you will see the magic happening.
package com.marco.business.logic;

public class SomeLogic {

        private static final Logger LOGGER = Logger.getLogger(SomeLogic.class);

        @CommitTransaction
        public void executeSomeLogic(Object someObject) {
             // a transaction finished
        }
}