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

How To: GitHub projects in Spring Tool Suite

Spring 3 Part 7: Spring with Databases

Parse the namespace based XML using Python