5 minutes with – Spring AOP

Another interesting feature in Spring Framework is the Aspect Oriented Programming (AOP). In my opinion this feature is very useful for adding some behaviours into the code without edit it inside, for example,  the method’s implementation.

In this article I’ll show you how integrate this feature inside a Spring Web Application.

Is there anything better than an example to explain why you could use something? Let me show you this simple task.

You have to check your code method to take execution time. A normale  approach of this would be:


public void methodA()

{
log.info("Start time ....");
...
log.info("Stop time ....");

}

This approach works fine. Ok, but it’s a very expensive way(every method should be marked with this log) and the changes are inside the method implementation.

A better way is available with spring framework and AOP.

This is the Logging class with AOP.


package it.samplewebaopspring.aop;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;
import org.apache.log4j.Logger;
public class OrderLogging implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice, MethodInterceptor{

 /** Time in milliseconds */
 long startTime = 0;

 /** Time in milliseconds */
 long finishTime = 0;
 Logger log = Logger.getLogger(this.getClass());

 @Override
 public void before(Method method, Object[] args, Object target)
   throws Throwable {
  startTime = System.currentTimeMillis();
  log.info("Executing method " + method.getName()
   + " on object " + target.getClass().getName());

  
 }

 @Override
 public void afterReturning(Object returnValue,
   Method method, Object[] args, Object target) throws Throwable {
  
  finishTime = System.currentTimeMillis();
  double totalDuration = finishTime - startTime;
  log.info("Finished executing method " + method.getName()
   + " on object " + target.getClass().getName() + " in "
   + totalDuration / 1000 + " seconds.");

  
 }
 public void afterThrowing(Method method, Object[] args, Object target, Exception exception){
  log.error(exception);
 }

 @Override
 public Object invoke(MethodInvocation arg0) throws Throwable {
  log.info("Method invoked with params:" + arg0.getArguments()[0]);
  return arg0.proceed();
 }

}

The 4 interfaces implemented are:

  • org.aopalliance.intercept.MethodInterceptor: called when the method is invoked;
  • org.aopalliance.intercept.MethodInvocation: called before the method execution;
  • org.springframework.aop.AfterReturningAdvice: called after the method execution;
  • org.springframework.aop.ThrowsAdvice: called when the method invokes an exception.

We’d like to take a times for our order process. Here the interface of our business model.


package it.samplewebaopspring.order;

import it.samplewebaopspring.bean.Order;

public interface IOrderProcess {
 public void Check(Order order) throws Exception;
 public Order Process(Order order);
 public Order Shipping(Order order);

}

And here the implementation


package it.samplewebaopspring.order;

import java.util.Date;

import it.samplewebaopspring.bean.Order;

public class OrderProcess implements IOrderProcess {

 @Override
 public void Check(Order order) throws Exception {
  if (order.getItemNumber()==0)
  {
   throw new Exception("Quantity cannot be 0!!");
  }  
 }

 @Override
 public Order Process(Order order) {
  order.setInvoice(order.getItemNumber() * 1.3);
  return order;
 }

 @Override
 public Order Shipping(Order order) {
  order.setShippingDate(new Date());
  return order;  
 }
}

I used a Spring MVC to show the information. This is the controller


package it.samplewebaopspring.controller;
import it.samplewebaopspring.bean.Order;
import it.samplewebaopspring.order.OrderProcess;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class OrderController {
 
 @Autowired
 private OrderProcess orderProcess;
 
 Logger log = Logger.getLogger(this.getClass());
 
 @RequestMapping(value="/order")
 public String getOrder(ModelMap model) {

  Order order = new Order();
  order.setOrderId("CK-1244");
  order.setItemNumber(5);
  
  try {
   orderProcess.Check(order);
  } catch (Exception e) {
   log.error(e);
  }
  log.info("******************************************");
  order = orderProcess.Process(order);
  log.info("******************************************");
  order = orderProcess.Shipping(order);
  
  model.addAttribute("detail", order);
  return "order";

 }
}

The key point is the spring 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" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.0.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

	<context:component-scan base-package="it.samplewebaopspring.controller" />

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

	<bean id="orderProcess" class="it.samplewebaopspring.order.OrderProcess" />

	<bean id="performanceLoggingAdvice" class="it.samplewebaopspring.aop.OrderLogging" />

	<aop:config>
		<!-- Performance logging pointcut - run for every method in a control or
			facade class -->
		<aop:pointcut id="performanceLoggingPointcut" expression="execution(* it.samplewebaopspring.order.*.*(..))" />

		<aop:advisor advice-ref="performanceLoggingAdvice"
			pointcut-ref="performanceLoggingPointcut" id="performanceLoggingInterceptorAdvisor" />
	</aop:config>

	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix">
			<value>/WEB-INF/pages/</value>
		</property>
		<property name="suffix">
			<value>.jsp</value>
		</property>
	</bean>

</beans>

A very important thing is proxy-target-class=”true” to avoid problem with @Autowired object.

Take a look at the log output (it’s more interesting than browser output for this example).

INFO  it.samplewebaopspring.aop.OrderLogging - invoke with params: orderId:CK-1244 itemNumber:5 invoice:0.0 shippingDate:null
INFO  it.samplewebaopspring.aop.OrderLogging - before Check on object it.samplewebaopspring.order.OrderProcess
INFO  it.samplewebaopspring.aop.OrderLogging - afterReturning Check on object it.samplewebaopspring.order.OrderProcess in 0.672 seconds.
INFO  it.samplewebaopspring.controller.OrderController - ******************************************
INFO  it.samplewebaopspring.aop.OrderLogging - invoke with params: orderId:CK-1244 itemNumber:5 invoice:0.0 shippingDate:null
INFO  it.samplewebaopspring.aop.OrderLogging - before Process on object it.samplewebaopspring.order.OrderProcess
INFO  it.samplewebaopspring.aop.OrderLogging - afterReturning Process on object it.samplewebaopspring.order.OrderProcess in 1.125 seconds.
INFO  it.samplewebaopspring.controller.OrderController - ******************************************
INFO  it.samplewebaopspring.aop.OrderLogging - invoke with params: orderId:CK-1244 itemNumber:5 invoice:6.5 shippingDate:null
INFO  it.samplewebaopspring.aop.OrderLogging - before Shipping on object it.samplewebaopspring.order.OrderProcess
INFO  it.samplewebaopspring.aop.OrderLogging - afterReturning Shipping on object it.samplewebaopspring.order.OrderProcess in 0.578 seconds.

Some references are available at Spring official web site (http://static.springsource.org/spring/docs/3.0.x/reference/aop.html) and in many example website.

To be honest, you have to know that I don’t like this feature very much. The reason is about the chance of not correct use of it in the application (do you remember “goto” instruction?).

I’m sure this is a good feature but I wouldn’t consider this as substitute of good refactory when it needed.

Advertisements

3 thoughts on “5 minutes with – Spring AOP

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s