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
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.
You can download the source code from the GitHub.
Comments
Post a Comment
commented your blog