Wednesday, April 4, 2012

Spring: Spring AOP with XML Configuration

Overview:
This post will look how you could easily configure Spring AOP. Also, this article tries to focus on XML base configuration rather than AspectJ annotations. Moreover, XML based configuration will not have any restrictions on the under lying JDK version (e.i; for annotation based configuration to work, we require JDK 1.5 or higher). Sample code can be found here.

We'll be looking at the following AOP configurations,
  • Before - Runs before the execution of a method.
  • After - Runs after the execution of a method.
  • AfterReturning - Runs after the method returns a result.
  • AfterThrowing - Runs after the methods throws an exception.
  • Around - Runs whilst the method is executing.
Project Structure: Following diagram show the project structure I have used for this post.



Step 1: Maven Dependencies
 <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.fazlan</groupId>
<artifactId>spring-aop</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>spring-aop</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- JUnit testing framework -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${org.aspectj.version}</version>
</dependency>
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>${org.aspectj.version}</version>
</dependency>
</dependencies>
<properties>
<org.aspectj.version>1.6.11</org.aspectj.version>
<org.springframework.version>3.1.1.RELEASE</org.springframework.version>
</properties>
</project>
Step 2: Creating POJO Classes and Service Implementation
Person.java
 public class Person {
private long id;
private String firstName;
private String lastName;
private static long ID_INDEXER = 1;
public Person() {
id = ID_INDEXER++;
}
public Person(String firstName, String lastName) {
this();
this.firstName = firstName;
this.lastName = lastName;
}
public long getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}
Service Interface:
 public interface ICRUDService<T> {
Serializable save(T t);
void update(T t);
T get(Serializable id);
List<T> getAll();
}
Service Implementation:
 public class PersonService implements ICRUDService<Person>{
private static final Map<Serializable, Person> PERSON_MAP = new HashMap<Serializable, Person>();
public Serializable save(Person person) {
update(person);
return person.getId();
}
public void update(Person person) {
PERSON_MAP.put(person.getId(), person);
}
public Person get(Serializable id) {
return PERSON_MAP.get(id);
}
public List<Person> getAll() {
return new ArrayList<Person>(PERSON_MAP.values());
}
}

Step 3: Creating the Aspect Class
 public class LoggerAspect {
public void before(JoinPoint joinpoint) {
System.out.println("Log before of method --:> " + joinpoint.getSignature().getName());
if (joinpoint.getArgs().length > 0) {
 System.out.println(joinpoint.getArgs()[0]);
}
System.out.println("---------------------------------");
}
public void after(JoinPoint joinpoint) {
System.out.println("Log after of method --:> " + joinpoint.getSignature().getName());
System.out.println("---------------------------------");
}
public Object around(ProceedingJoinPoint joinpoint) throws Throwable {
long t1 = System.currentTimeMillis();
System.out.println("Log proceed of method --:> " + joinpoint.getSignature().getName());
Object obj = joinpoint.proceed();
System.out.println("Time taken to execute : " + (System.currentTimeMillis() - t1) + " milliseconds.");
System.out.println("---------------------------------");
return obj;
}
public void afterReturning(JoinPoint joinpoint, Object result) {
System.out.println("Log afterReturning of method --:> " + joinpoint.getSignature().getName()
   + " [" + result + "]");
System.out.println("---------------------------------");
}
public void afterThrowing(JoinPoint joinpoint, Throwable error) {
System.out.println("Log afterThrowing of method --:> " + joinpoint.getSignature().getName()
   + " [" + error.getMessage() + "]");
System.out.println("---------------------------------");
}
}

Step 4: Configuring the Spring Beans and Wiring the AOP to the Service Implementation
 <?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/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="personService" class="org.fazlan.spring.service.PersonService"/>
<!-- AOP Aspects-->
<bean id="loggerAspect" class="org.fazlan.spring.aop.LoggerAspect"/>
<aop:config>
<aop:pointcut id="personServicePC" expression="execution(* org.fazlan.spring.service.ICRUDService.*(..))"/>
<aop:aspect id="preLoggingAspects" ref="loggerAspect">
  <!-- @Before Aspect-->
  <aop:before method="before" pointcut-ref="personServicePC"/>
  <!-- @After Aspect-->
  <aop:after method="after" pointcut-ref="personServicePC"/>
  <!-- @After Aspect-->
  <aop:around method="around" pointcut-ref="personServicePC"/>
  <!-- @AfterReturning Aspect-->
  <aop:after-returning method="afterReturning" returning="result" pointcut-ref="personServicePC"/>
  <!-- @AfterThrowing Aspect-->
  <aop:after-throwing method="afterThrowing" throwing="error" pointcut-ref="personServicePC"/>
</aop:aspect>
</aop:config>
</beans>

Step 5: Trying out the AOP with a Simple Application Client
 public class Main {
public static void main(String... args) {
 ApplicationContext context =
 new ClassPathXmlApplicationContext("org/fazlan/spring/aop/spring-conf-application.xml");
 ICRUDService<Person> service = (ICRUDService<Person>) context.getBean("personService");
 Serializable id = service.save(new Person("First Name", "Second Name"));
 Person p1 = service.get(id);
 p1.setFirstName("Updated First Name");
 service.update(p1);
 service.save(new Person("Fazlan", "Sabar"));
 for(Person p: service.getAll()) {
   System.out.println(p);
 }
}
}
Sample output Generated:
 Apr 4, 2012 1:03:19 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [org/fazlan/spring/aop/spring-conf-application.xml]
Apr 4, 2012 1:03:19 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@56c163f: defining beans [personService,loggerAspect,org.springframework.aop.config.internalAutoProxyCreator,personServicePC,org.springframework.aop.aspectj.AspectJPointcutAdvisor#0,org.springframework.aop.aspectj.AspectJPointcutAdvisor#1,org.springframework.aop.aspectj.AspectJPointcutAdvisor#2,org.springframework.aop.aspectj.AspectJPointcutAdvisor#3,org.springframework.aop.aspectj.AspectJPointcutAdvisor#4]; root of factory hierarchy
Log before of method --:> save
Person{id=1, firstName='First Name', lastName='Second Name'}
---------------------------------
Log proceed of method --:> save
Log after of method --:> save
---------------------------------
Time taken to execute : 1 milliseconds.
---------------------------------
Log afterReturning of method --:> save [1]
---------------------------------
Log before of method --:> get
1
---------------------------------
Log proceed of method --:> get
Log after of method --:> get
---------------------------------
Time taken to execute : 0 milliseconds.
---------------------------------
Log afterReturning of method --:> get [Person{id=1, firstName='First Name', lastName='Second Name'}]
---------------------------------
Log before of method --:> update
Person{id=1, firstName='Updated First Name', lastName='Second Name'}
---------------------------------
Log proceed of method --:> update
Log after of method --:> update
---------------------------------
Time taken to execute : 0 milliseconds.
---------------------------------
Log afterReturning of method --:> update [null]
---------------------------------
Log before of method --:> save
Person{id=2, firstName='Fazlan', lastName='Sabar'}
---------------------------------
Log proceed of method --:> save
Log after of method --:> save
---------------------------------
Time taken to execute : 0 milliseconds.
---------------------------------
Log afterReturning of method --:> save [2]
---------------------------------
Log before of method --:> getAll
---------------------------------
Log proceed of method --:> getAll
Log after of method --:> getAll
---------------------------------
Time taken to execute : 0 milliseconds.
---------------------------------
Log afterReturning of method --:> getAll [[Person{id=1, firstName='Updated First Name', lastName='Second Name'}, Person{id=2, firstName='Fazlan', lastName='Sabar'}]]
---------------------------------
Person{id=1, firstName='Updated First Name', lastName='Second Name'}
Person{id=2, firstName='Fazlan', lastName='Sabar'}  

Summary:
We looked at how Spring framework allows us to easily configure AOP via XML with little configuration.

No comments:

Post a Comment