Tuesday, April 29, 2014

AOP in Spring

Aspect Oriented Programming:

Before dive into Spring AOP, let me explain the problems faced in my previous projects(.Ours is an Internet Banking solution.So we need to audit the important fund transfer actions(SaveFT, SubmitFT,AuthorizeFT and RejectFT) in case if something goes wrong, the bank can see the audit and find out who saved or submitted a particular operation.So they can do the necessary actions.

Coming to auditing functionality, this is scattered in most of the classes and any changes to the audit functionality we need to change all classes it was implemented.So even though my actual business logic wasn't change we need to change those classes and need to compile and deploy again which is pain and error prone.Some of the cross cutting functionalities are logging,security,auditing and transaction management.

AOP on the other hand provide solution to the above cross cutting concerns or cross cutting functionalists.
We dont need to write these concerns in side our business classes, we can write these aspects separately and run our advices at run time at the desired target methods.AOP also reduces the boiler plate code across our application.

Terminology in Spring AOP;

1)Aspect: Aspect is the cross cutting functionality in our application like logging,auditing,transaction or security.

2)Advice: Advice is the job of an Aspect.Advice will define what and when of an aspect.In addition to describing the job of an aspect will perform, advice addresses the question of when to perform the job

Spring aspects can work with five kinds of advices

a)Before: Advice will run before the invocation of our target method.
b)After: Advice will run after the completion of our target method regardless of the outcome.
c)After-returning: The Advice functionality takes place after the target method completed successfully.
d)After-throwing: The Advice funcionality takes place after the target method throws an exception.
e)Around: Advice will run before and after our target method.

3)Join Points: A join point is 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.But in Spring join points only represent methods.We can use join points and methods synonymously.
These are the points where your aspect’s code can be inserted into the normal flow of your application to add new behavior.

4)Pointcut: Pointcuts are used to pinpoint where an aspect’s advice should be applied. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP.

Lets write a small Logging aspect to understand AOP implementation in Spring.

For example i have a bank account and i want to run this logging functionality to run before and after the deposit method.

BankAccount.java


package com.ramesh.domain;  
 public class BankAccount {  
      private long balance;  
      // method to deposit  
      public void deposit(int depositAmount) {  
           if (depositAmount < 0) {  
                System.out.print("Invalid Amount!");  
           }  
           balance = balance + depositAmount;  
           System.out.println("Current Balance is "+balance);  
      }  
      // method to withdraw  
      public int withdraw(int withdrawAmount) {  
           System.out.println("Your Balance is: " + balance);  
           if (withdrawAmount > balance) {  
                System.out.println("You do not have a sufficient balance to withdraw this amount!");  
           }  
           return withdrawAmount;  
      }  
 }  

LoggingAspect.java:

This aspect has two advices which we want to run BankAccount.java's deposit method.Keep in mind the BankAccount doesnt aware about this logging aspect.Spring creates a proxy and weaves these advices in the runtime.

package com.ramesh.domain;  
 public class LoggingAspect {  
      public void beforeDeposit() {  
           System.out.println("Running before deposit method");  
      }  
      public void afterDeposit() {  
           System.out.println("Running After Deposit method");  
      }  
 }  


Now lets configure our application-context.xml to include these two bean definitions and configure the before and after pointcuts.

 <?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="bankAccount" class="com.ramesh.domain.BankAccount" />  
      <bean id="logging" class="com.ramesh.domain.LoggingAspect" />  
      <aop:config>  
           <aop:aspect ref="logging">  
                <aop:before pointcut="execution(* com.ramesh.domain.BankAccount.deposit(..))"  
                     method="beforeDeposit" />  
                <aop:after pointcut="execution(* com.ramesh.domain.BankAccount.deposit(..))"  
                     method="afterDeposit" />  
           </aop:aspect>  
      </aop:config>  
 </beans>  


We are configuring the aop in xml file.We can do this with annotations also.

Here we declared these two beans.There is no dependency between these two beans.we are defining logging bean as an aspect. and we want to execute before method at our target method com.ramesh.domain.BankAccount.deposit. please note * at starting the pointcut denotes we never care about the return type and deposit(..) indicates the method can take any number of parameters.

Since we completed our configuration lets test it..

package com.ramesh;  
 import org.springframework.context.ApplicationContext;  
 import org.springframework.context.support.ClassPathXmlApplicationContext;  
 import com.ramesh.domain.BankAccount;  
 public class MainApp {  
      public static void main(String[] args) {  
           ApplicationContext appContext = new ClassPathXmlApplicationContext(  
                     "spring-context.xml");  
           BankAccount account = (BankAccount) appContext.getBean("bankAccount");  
           account.deposit(100);  
      }  
 }  


If you see the main program, we only calling the deposit method.Lets see the output after running this

Apr 29, 2014 8:50:33 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1c0e45a: defining beans [bankAccount,logging,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.aop.aspectj.AspectJPointcutAdvisor#0,org.springframework.aop.aspectj.AspectJPointcutAdvisor#1]; root of factory hierarchy


Running before deposit method
Current Balance is 100
Running After Deposit method

Magic...our advices(beforeDeposit() and afterDeposit() methods) run before and after our target method(deposit())..

we have seen before and after advice.Now lets see around advice.

Around advice will run before and after the target method.This advice will be useful when we want to save the state between the before and after advice calls, if we don't have around advice we need to save the state to a variable when before and after advices used.

Around advice will take ProceedingJoinPoint as an parameter and it has proceed method will is used to pass the control to our target method.

Lets say we want to run around advice to a target method withdraw in BankAccount

package com.ramesh.domain;  
 public class BankAccount {  
      private long balance=1000;  
      // method to deposit  
      public void deposit(int depositAmount) {  
           if (depositAmount < 0) {  
                System.out.print("Invalid Amount!");  
           }  
           balance = balance + depositAmount;  
           System.out.println("Current Balance is "+balance);  
      }  
      // method to withdraw  
      public long withdraw(int withdrawAmount) {  
           System.out.println("Your Balance is: " + balance);  
           if (withdrawAmount > balance) {  
                System.out.println("You do not have a sufficient balance to withdraw this amount!");  
           }  
           balance=balance-withdrawAmount;  
           return balance;  
      }  
 }  


 package com.ramesh.domain;  
 import org.aspectj.lang.ProceedingJoinPoint;  
 public class LoggingAspect {  
      public void beforeDeposit() {  
           System.out.println("Running before deposit method");  
      }  
      public void afterDeposit() {  
           System.out.println("Running After Deposit method");  
      }  
      public void aroundWithdraw(ProceedingJoinPoint joinPoint) {  
           System.out  
                     .println("Inside around method: It will run before advised method");  
           try {  
                joinPoint.proceed();  
           } catch (Throwable e) {  
                e.printStackTrace();  
           }  
           System.out  
                     .println("Inside around method: It will run after advised method");  
      }  
 }  


Lets see the around configuration in spring-context.xml

<?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="bankAccount" class="com.ramesh.domain.BankAccount" />  
      <bean id="logging" class="com.ramesh.domain.LoggingAspect" />  
      <aop:config>  
           <aop:aspect ref="logging">  
                <aop:around pointcut="execution(* com.ramesh.domain.BankAccount.withdraw(..))"  
                     method="aroundWithdraw" />  
           </aop:aspect>  
      </aop:config>  
 </beans>  


Lets run the MainApp.java to the output

package com.ramesh;  
 import org.springframework.context.ApplicationContext;  
 import org.springframework.context.support.ClassPathXmlApplicationContext;  
 import com.ramesh.domain.BankAccount;  
 public class MainApp {  
      public static void main(String[] args) {  
           ApplicationContext appContext = new ClassPathXmlApplicationContext(  
                     "spring-context.xml");  
           BankAccount account = (BankAccount) appContext.getBean("bankAccount");  
           account.withdraw(100);  
      }  
 }  


Output:

Apr 30, 2014 12:27:32 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@982589: defining beans [bankAccount,logging,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.aop.aspectj.AspectJPointcutAdvisor#0]; root of factory hierarchy


Inside around method: It will run before advised method
Your Balance is: 1000
Inside around method: It will run after advised method


Thanks for reading!!!!!!

No comments:

Post a Comment