I am Siva, a passionate java developer, open source enthusiast, blogger. I like to cover Java, Struts, Spring, Hibernate, Ajax Tutorials, How-To's and Best Practices. Sivaprasadreddy is a DZone MVB and is not an employee of DZone and has posted 39 posts at DZone. You can read more from them at their website. View Full User Profile

Aspect Oriented Programming Using SpringAOP

01.18.2011
| 12524 views |
  • submit to reddit

While developing software applications for a business we receive the requirements either from a requirements gathering team or from business analysts. In general those requirements are functional requirements which represent the activities that the business is doing. Apart from the functional requirements we should also consider some other points like performance, transaction management, security, logging etc. These are called non-functional requirements.

Let us consider a BookStore application which is providing web access to the store. The user can browse the various categories of books, add books to the cart and finally checkout, do payment and get the books.

For this app we might recieve the requirements from business analyst as follows:
1. A login/registration screen to enter into BookStore.
2. Users should be able to browse thruogh verious categories of books
3. Users should be able tosearch books by name, author name, publisher
4. Users should be able to add/remove the books to/from his cart
5. Users should be able to see what are all the items currently in his cart
6. Users should be able to checkout and provide facility to pay the amount through some payment gateway
7. A successful message will be shown to users with all the details of his purchases.
8. A failure message will be shown to users with the cause of failure.
9. BookStore administrator/manager should be able to provide the access to add/remove/update book details.

All the above requirements fall under Functional Requirements. While implementing the above we should also take care of the following things even though they are not explicitly mentioned:

  1. Role based access to UI. Here only Administrators/Managers can access have access to add/remove/update book details.[Role based Authorization]
  2. Atomicity in Purchasing. Suppose a user logged into the BookStore and add 5 books to his cart, checked out and did payment. In the back-end implementation we may need to enter this purchase details in 3 tables. If after inserting the data into 2 tables and system crashed the whole operation should be rolled-back.[Transaction Management].
  3. No one is perfect and no system is flawless. So if something went wrong and the development team has to figure it out what went wrong logging will be most useful. So logging should be implemented in such a way that developer should be able to figured it out where exactly it got failed and fix it.[Logging]

The above implicit requirements are called non-functional requirements.  In addition to the above performance will obviously be a crucial non-functional requirement for many public facing websites.

So with all the above functional requirements we can build the system decomposing the whole system into various components by taking care of non-functional requirements through out the components.

public class OrderService
{
private OrderDAO orderDAO;

public boolean placeOrder(Order order)
{
boolean flag =false;
logger.info("Entered into OrderService.placeOrder(order) method");
try
{
flag = orderDAO.saveOrder(order);
}
catch(Exception e)
{
logger.error("Error occured in OrderService.placeOrder(order) method");
}
logger.info("Exiting from OrderService.placeOrder(order) method");
return flag;
}
}

public class OrderDAO
{
public boolean saveOrder(Order order)
{
boolean flag =false;
logger.info("Entered into OrderDAO.saveOrder(order) method");
Connectoin conn = null;
try
{
conn = getConnection();//get database connection
conn.setAutoCommit(false);
// insert data into orders_master table which generates an order_id
// insert order details into order_details table with the generated order_id
// insert shipment details into order_shipment table
conn.commit();
conn.setAutoCommit(true);
flag = true;
}
catch(Exception e)
{
logger.error("Error occured in OrderDAO.saveOrder(order) method");
conn.rollback();
}
logger.info("Exiting from OrderDAO.saveOrder(order) method");
return flag;
}
}


Here in the above code, the functional requirement implementation and non-functional requirement implementation is mingled in the same place.
Logging is placed across the OrderService and OrderDAO classes. Transaction Management is spanned across DAOs.
With this we will have several issues:

  1. The classes needs to be changed either to change functional or non-functional requirements.
    e.g. At some point later in the development if the Team decides to log the Method Entry/Exit information along with TimeStamp we need to change almost all the classes.
  2. The Transaction Management code setting the auto-commit to false in the beginning, doing the DB operations, committing/rollbacking the operation logic will be duplicated across all the DAOs.

Here if we see Method Entry/Exit logging is spanned across all the modules. Transaction Management is spanned across all the DAO's.

These kind of requirements which span across the modules/components is called Cross Cutting Concerns.

To better design the system we should separate out these cross cutting concerns from actual business logic so that it will be easier to change or enhance or maintain later point of time.

Aspect Oriented Programming is a methodology which says separate the cross cutting concerns from actual business logic.

So let us follow AOP methodology and redesign the above two classes separating the cross cutting concerns.

public interface IOrderService
{
public boolean placeOrder(Order order);
}

public class OrderService implements IOrderService
{
private OrderDAO orderDAO;

public boolean placeOrder(Order order)
{
return orderDAO.saveOrder(order);
}
}


public class OrderDAO
{
public boolean saveOrder(Order order)
{
boolean flag =false;

Connectoin conn = null;
try
{
conn = getConnection();//get database connection
// insert data into orders_master table which generates an order_id
// insert order details into order_details table with the generated order_id
// insert shipment details into order_shipment table
flag = true;
}
catch(Exception e)
{
logger.error(e);
}
return flag;
}
}
Now lets create a LoggingInterceptor implementing how logging should be done and create a Proxy for OrderService which takes the call from caller, log the entry/exit entries using LoggingInterceptor and delegates to actual OrderService.

By using Dynamic Proxies we can separate out implementation of cross cutting concerns(Logging) from actual business logic as follows.
public class LoggingInterceptor
{
public void logEntry(Method m)
{
logger.info("Entered into "+m.getName()+" method");
}
public void logExit(Method m)
{
logger.info("Exiting from "+m.getName()+" method");
}
}

public class OrderServiceProxy implements IOrderService extends LoggingInterceptor
{
private OrderService orderService;

public boolean placeOrder(Order order)
{
boolean flag =false;
Method m = getThisMethod();//get OrderService.placeOrder() Method object
logEntry(m);
flag = orderService.placeOrder(order);
logExit(m);
return flag;
}
}


Now the OrderService caller(OrderController) can get the OrderServiceProxy and place the order as:

public class OrderController
{
public void checkout()
{
Order order = new Order();
//set the order details
IOrderService orderService = getOrderServiceProxy();
orderService.placeOrder(order);
}
}

We have several AOP frameworks to seperate out implementation of cross cutting concerns.
a)Spring AOP
b)AspectJ
b)JBoss AOP

Now lets see how we can separate out Logging from actual business logic using Spring AOP.

Before going to use Spring AOP, first we need to understand the following:

JoinPoint: A joinpoint is a point in the execution of the application where an aspect can be plugged in. This point could be a method being called, an exception being thrown, or even a field being modified.

Pointcut: A pointcut definition matches one or more joinpoints at which advice should be woven. Often you specify these pointcuts using explicit class and method names or through regular expressions that define matching class and method name patterns.

Aspect: An aspect is the merger of advice and pointcuts.

Advice:The job of an aspect is called advice.

SpringAOP supports several types of advices:
    1. Before: This advice weaves the aspect before method call.
    2. AfterReturning: This advice weaves the aspect after method call.
    3. AfterThrowing: This advice weaves the aspect when method throws an Exception.
    4. Around: This advice weaves the aspect before and after method call.
 

Suppose we have the following ArithmeticCalculator interface and implementation classes.
package com.springapp.aop;
public interface ArithmeticCalculator
{
public double add(double a, double b);
public double sub(double a, double b);
public double mul(double a, double b);
public double div(double a, double b);
}

package com.springapp.aop;
import org.springframework.stereotype.Component;

@Component("arithmeticCalculator")
public class ArithmeticCalculatorImpl implements ArithmeticCalculator
{
public double add(double a, double b)
{
double result = a + b;
System.out.println(a + " + " + b + " = " + result);
return result;
}

public double sub(double a, double b)
{
double result = a - b;
System.out.println(a + " - " + b + " = " + result);
return result;
}

public double mul(double a, double b)
{
double result = a * b;
System.out.println(a + " * " + b + " = " + result);
return result;
}

public double div(double a, double b)
{
if(b == 0)
{
throw new IllegalArgumentException("b value must not be zero.");
}
double result = a / b;
System.out.println(a + " / " + b + " = " + result);
return result;
}
}

The following LoggingAspect class shows various bit and pieces of applying Logging Advice using SpringAOP.
package com.springapp.aop;

import java.util.Arrays;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect
{
private Log log = LogFactory.getLog(this.getClass());

@Pointcut("execution(* *.*(..))")
protected void loggingOperation() {}

@Before("loggingOperation()")
@Order(1)
public void logJoinPoint(JoinPoint joinPoint)
{
log.info("Join point kind : " + joinPoint.getKind());
log.info("Signature declaring type : "+ joinPoint.getSignature().getDeclaringTypeName());
log.info("Signature name : " + joinPoint.getSignature().getName());
log.info("Arguments : " + Arrays.toString(joinPoint.getArgs()));
log.info("Target class : "+ joinPoint.getTarget().getClass().getName());
log.info("This class : " + joinPoint.getThis().getClass().getName());
}

@AfterReturning(pointcut="loggingOperation()", returning = "result")
@Order(2)
public void logAfter(JoinPoint joinPoint, Object result)
{
log.info("Exiting from Method :"+joinPoint.getSignature().getName());
log.info("Return value :"+result);
}

@AfterThrowing(pointcut="execution(* *.*(..))", throwing = "e")
@Order(3)
public void logAfterThrowing(JoinPoint joinPoint, Throwable e)
{
log.error("An exception has been thrown in "+ joinPoint.getSignature().getName() + "()");
log.error("Cause :"+e.getCause());
}

@Around("execution(* *.*(..))")
@Order(4)
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable
{
log.info("The method " + joinPoint.getSignature().getName()+ "() begins with " + Arrays.toString(joinPoint.getArgs()));
try
{
Object result = joinPoint.proceed();
log.info("The method " + joinPoint.getSignature().getName()+ "() ends with " + result);
return result;
} catch (IllegalArgumentException e)
{
log.error("Illegal argument "+ Arrays.toString(joinPoint.getArgs()) + " in "+ joinPoint.getSignature().getName() + "()");
throw e;
}
}

}

applicationContext.xml
 <beans>

<context:annotation-config/>
<context:component-scan base-package="com.springapp"></context:component-scan>
<aop:aspectj-autoproxy/>

</beans>
And a standalone test client to test the functionality.
package com.springapp.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringAOPClient
{

public static void main(String[] args)
{
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ArithmeticCalculator calculator = (ArithmeticCalculator) context.getBean("arithmeticCalculator");
double sum = calculator.add(12, 23);
System.out.println(sum);
double div = calculator.div(1, 10);
System.out.println(div);
}

}

Required jars:

  • Spring.jar(2.5.6 or above)
  • commons-logging.jar
  • aopalliance.jar
  • aspectjrt.jar
  • aspectjweaver.jar
  • cglib-nodep-2.1_3.jar

We can define the type of advice using @Before, @AfterReturning, @Around etc. We can define pointcuts in different ways.
@Around("execution(* *.*(..))") means it is an Around advice which will be applied to all classes in all packages and all methods.
Suppose if we want to apply it only for all the services residing in the com.myproj.services package, the pointcut would be

@Around("execution(* com.myproj.services.*.*(..))").

"(..)" means with any type of arguments.

If we want to apply same pointcuts for many advices we can define a pointcut on a method and can refer that later as follows.

 @Pointcut("execution(* *.*(..))")
protected void loggingOperation() {}

@Before("loggingOperation()")
public void logJoinPoint(JoinPoint joinPoint)
{
}

If multiple Advices have to be applied on the same pointcut we can specify the order using @Order on which advices will be applied. In the above example @Before will be applied first then @Around will be applied when add() method is called.

With AOP approach the code will be more cleaner and maintainable. SpringAOP is one way of implementing AOP and only supports method invokation join points. AspectJ is even more powerful and can be applied on several additional joinpoints. Spring also supports AspectJ integration.

From: http://sivalabs.blogspot.com/2011/01/aspect-oriented-programming-using.html

 

AttachmentSize
SpringAOP.zip9.71 KB
Published at DZone with permission of Sivaprasadreddy Katamreddy, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Raghavendra Poola replied on Tue, 2011/01/18 - 5:34am

Great work Shiva. Its really very tough to put something(what we know) on paper, especially these kind of concepts.

Its pretty clear to me and expecting more posts from you in this blog. 

 

I am a biggener to JAVA as well as Spring, so can you please explain what do you mean by proxy.

I am reffering to below one:

"Now lets create a LoggingInterceptor implementing how logging should be done and create a Proxy for OrderService"

Sivaprasadreddy... replied on Tue, 2011/01/18 - 6:52am in response to: Raghavendra Poola

@Raghavendra,

A proxy is an object which holds a reference to an internal object and behaves like the internal object exposing same methods to the caller. The proxy object is implemented by delegating method calls to the internal object. 

Guillaume Tricot replied on Tue, 2011/01/18 - 11:34am

Great article Sivaprasadreddy !

Aspect Oriented Programming was a black hole for me and now everything seems very clear...

Thank's !

Josh Marotti replied on Tue, 2011/01/18 - 12:07pm

Good one.  Always hard to explain and you did well.  A good follow up would be how too much AOP is a bad thing (magic things happening to the code that the developer would be unaware of).

Jonathan Fisher replied on Tue, 2011/01/18 - 12:28pm

AOP kinda stinks without SpringAOP... We're addicted!

Sivaprasadreddy... replied on Wed, 2011/01/19 - 4:23am

@Guillaume, @Josh,

Thanks for your feedback.

@Josh,

As you said excessive use of AOP will complicate the things and leads to magical things happening.

When not to use AOP deserves a separate post :-)

Josh Marotti replied on Wed, 2011/01/19 - 11:30am in response to: Sivaprasadreddy Katamreddy

Absolutely agree, which is why I said it'd be a good follow up ;)


Again, though, great article! :)

Khent Johnson replied on Thu, 2011/09/01 - 9:01am

Yes, I agree same with the other commentators here this article contains great piece of information that's really helpful to all IT related workers. And I am one of the beneficiaries here. lol Anyway, identifying the project's requirements from functional to non-functional requirements is very important throughout the development. Reputation Advocate

Jessie Mear replied on Wed, 2011/09/07 - 6:38am

AOP is a promising technology for separating crosscutting concerns, something usually hard to do in object-oriented programming. java programmers

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.