Spring 3 part 6: Spring AOP

This is the 6th part of the Spring 3 Series.

AOP Concepts

  • Joinpoint: Joinpoint is the point in an application at which additional logic can be inserted. For example method invocation that is the only Spring support joinpoint type.
  • Advice: Advice is executed at a particular joinpoint in an application. NOTE: no advices for final classes. All the advisors need to implement the Advisor interface. There are two kind of advisors available in the Spring: IntrodcutionAdvisor and PointcutAdvisor. Spring defines 6 difference advices: before(MethodBeforeAdvice),  after returning (AfterReturningAdvice), after(AfterAdvice), around (MethodInterceptor), Throws (ThrowsAdvice), and Introduction (IntroductionInterceptor).
  • Pointcuts: A pointcut is a collection of joinpoints that define when advice should be executed. For example, collection of all method invocations in a particular class.
  • Aspect: An aspect is the combination of advice and pointcuts. This combination immerge the logic which we used to execute in the application.
  • Weaving: In compile-time or runtime, process of actually inserting aspects into the application code is called weaving.
  • Target: Target is an object whose execution follow is modified by AOP process.
  • Introduction: Introducing new methods or field to modify the structure of the existing object.

Advices

aop-class-diagram

There are 6 advices as shown in the above diagram. you can create advices for the above implementations.

Here some basic thing you should know

  • To enable @AspectJ support with XML based configuration use the <aop:aspectj-autoproxy>
  • Declare an aspect either in the config xml as regular bean or @Aspect in the bean (@Aspect is not sufficient to detect the aspect from the classpath, therefore need to use @Component).
  • Spring only support method execution joint points. Pointcut include two parts, pointcut expression and the method signature.  Use either @Pointcut or <aop:config> and <aop:advicesor>. It is best practice to build more complex pointcut expressions out of smaller named components.
  • Declare an advice kind of advice shown in the above figure.

According to the spring reference, @AspectJ is recommended to use in regular applications.

Example

Let’s see the example. This is very simple example: the Hello service with one single method that is sayHello(). This example includes the unit testing.

Here the service interface

package au.com.ojitha.blogspot.example.part6;

public interface Hello<T> {

public boolean sayHello(T name);

}

Then the Service

package au.com.ojitha.blogspot.example.part6;

import org.springframework.stereotype.Component;

@Component
public class HelloService implements Hello<Message> {

/* (non-Javadoc)
* @see au.com.ojitha.blogspot.example.part6.Hello#sayHello(java.lang.String)
*/

public boolean sayHello(Message name){
//System.out.println(name+", Hello");
return true;
}
}

The sayHello(..) method pass the Message as a parameter

package au.com.ojitha.blogspot.example.part6;

public class Message {
private String message;

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

Here the aspect with advices

package au.com.ojitha.blogspot.example.part6;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggerAspect {

Logger log = Logger.getLogger(LoggerAspect.class);

@Pointcut("execution(* au.com.ojitha.blogspot.example.part6.Hello+.sayHello(..)) &&"+"args(message,..)")
public void methodToBeLogged(Message message){}

@Before("methodToBeLogged(message)")
public void displayBefore(Message message){
log.info("Before Advice by "+message.getMessage());
}

@After("methodToBeLogged(message)")
public void displayAfter(Message message){
log.info("After Advice by "+message.getMessage());
}

@AfterReturning(pointcut="methodToBeLogged(message)", returning="retval")
public void displayAfterReturning(Message message, Object retval){
log.info("After Returning Advice by "+message.getMessage()+" and returned the value: "+retval);

}

@Around("methodToBeLogged(message)")
public boolean displayAround(ProceedingJoinPoint pjp, Message message) throws Throwable{
log.info("= Around Advice = before the method execute...");
boolean retval = (boolean)pjp.proceed();
log.info("= Around Advice = after the method executed...");
return retval;
}
}

In the above pointcut, “…Hello+.sayHello…”  shows the use of Java Generic interface.

Here the configuration file:

<?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:context="http://www.springframework.org/schema/context"
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.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

<description>Example configuration to get you started.</description>

<aop:aspectj-autoproxy/>
<context:component-scan base-package="au.com.ojitha.blogspot.example.part6" />

</beans>

As shown in the above listing, aspectj-autoproxy is the key player. In this example I am using JDK proxies only as shown in the following maven pom.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples.spring</groupId>
<artifactId>spring-utility</artifactId>
<version>1.0.0.CI-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Spring Utility</name>
<url>http://www.springframework.org</url>
<description>
<![CDATA[
This project is a minimal jar utility with Spring configuration.
]]>
</description>
<properties>
<maven.test.failure.ignore>false</maven.test.failure.ignore>
<spring.framework.version>3.2.2.RELEASE</spring.framework.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.2</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>

</plugins>
</build>
</project>

Here the simple test case to run

package au.com.ojitha.blogspot.example.part6;

import static org.junit.Assert.assertNotNull;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class HelloServiceConfigurationTests {
    
    Message message;

    @Autowired
    private Hello helloWorld;

    @Before
    public void setup(){
        message = new Message();
        message.setMessage("Ojitha");
    }
    
    @Test
    public void testSimpleProperties() throws Exception {
        assertNotNull(helloWorld);
        
    }
    
    @Test
    public void testSayHello(){
        assertNotNull(helloWorld.sayHello(this.message));
    }
    
}

 

Here the output,

22:02:53,811  INFO st.context.support.AbstractContextLoader: 216 - Detected default resource location "classpath:/au/com/ojitha/blogspot/example/part6/HelloServiceConfigurationTests-context.xml" for test class [au.com.ojitha.blogspot.example.part6.HelloServiceConfigurationTests].
22:02:53,815 INFO ort.AbstractDelegatingSmartContextLoader: 168 - GenericXmlContextLoader detected default locations for context configuration [ContextConfigurationAttributes@a377714 declaringClass = 'au.com.ojitha.blogspot.example.part6.HelloServiceConfigurationTests', locations = '{classpath:/au/com/ojitha/blogspot/example/part6/HelloServiceConfigurationTests-context.xml}', classes = '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
22:02:53,817 INFO pport.AnnotationConfigContextLoaderUtils: 111 - Could not detect default configuration classes for test class [au.com.ojitha.blogspot.example.part6.HelloServiceConfigurationTests]: HelloServiceConfigurationTests does not declare any static, non-private, non-final, inner classes annotated with @Configuration.
22:02:54,002 INFO eans.factory.xml.XmlBeanDefinitionReader: 315 - Loading XML bean definitions from class path resource [au/com/ojitha/blogspot/example/part6/HelloServiceConfigurationTests-context.xml]
22:02:54,165 INFO eans.factory.xml.XmlBeanDefinitionReader: 315 - Loading XML bean definitions from class path resource [META-INF/spring/app-context.xml]
22:02:54,376 INFO ontext.support.GenericApplicationContext: 510 - Refreshing org.springframework.context.support.GenericApplicationContext@801b120: startup date [Fri Mar 29 22:02:54 EST 2013]; root of context hierarchy
22:02:54,554 INFO ctory.support.DefaultListableBeanFactory: 596 - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@28826c2c: defining beans [org.springframework.aop.config.internalAutoProxyCreator,exampleService,helloService,loggerAspect,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
22:02:55,011 INFO itha.blogspot.example.part6.LoggerAspect: 40 - = Around Advice = before the method execute...
22:02:55,014 INFO itha.blogspot.example.part6.LoggerAspect: 24 - Before Advice by Ojitha
22:02:55,016 INFO itha.blogspot.example.part6.LoggerAspect: 42 - = Around Advice = after the method executed...
22:02:55,017 INFO itha.blogspot.example.part6.LoggerAspect: 29 - After Advice by Ojitha
22:02:55,018 INFO itha.blogspot.example.part6.LoggerAspect: 34 - After Returning Advice by Ojitha and returned the value: true
22:02:55,024 INFO ontext.support.GenericApplicationContext:1042 - Closing org.springframework.context.support.GenericApplicationContext@801b120: startup date [Fri Mar 29 22:02:54 EST 2013]; root of context hierarchy
22:02:55,026 INFO ctory.support.DefaultListableBeanFactory: 444 - Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@28826c2c: defining beans [org.springframework.aop.config.internalAutoProxyCreator,exampleService,helloService,loggerAspect,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy

In the above output line 8 to 12 shows the behaviour of the advices.

Get Adobe Flash player

You can download the source code from the GitHub.

Comments

Popular posts from this blog

Parse the namespace based XML using Python

How To: GitHub projects in Spring Tool Suite

EJB 3 JUnit testing with OpenEJB 3.1.4