Sunday, 18 November 2012

Spring AOP Sample code

Spring AOP Advice example

Spring AOP

As a developer we often consider concerns like business logic, performance, logging, security and so forth while implementing any module. If a single concern is implemented in multiple modules, it leads to lower code reuse and code scattering. For example consider a data access module. If one of the client code need authentication to access the database and another client code may need no authentication at all then placing the authentication code inside the database code might make our code unusable. This is where AOP comes into picture.

AOP (aspect oriented programming) is a programming methodology that enable the modularization of  concerns that cut across multiple types and objects.

AOP Terminologies:


  1. Aspect: a modularization of a concern that cuts across multiple classes. 
  2. Join point: a point during the execution of a program, such as the execution of a method.
  3. Advice: action taken by an aspect at a particular join point. Different types of advice include "around," "before", "after" advice etc. 
  4. Pointcut: a predicate that matches join points.  
Spring AOP supports 5 types of advices:

  1. Before advice: Advice that executes before a join point, but which does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).
  2. After returning advice: Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.
  3. After throwing advice: Advice to be executed if a method exits by throwing an exception.
  4. After (finally) advice: Advice to be executed regardless of the means by which a join point exits (normal or exceptional return).
  5. Around advice: Advice that surrounds a join point such as a method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.

Spring AOP Example

In this example, we are taking a scenario where there is a small party and how different advices can be used to cater different situations:







  • @Before advice - Will be used for authentication of guests. Only people in guest list can come. 
  • @Around advice - Will be used for Audit. Guest's entry and exit timings should be noted. 
  • @AfterThrowing - Will be used for Exception handling. If a guest gets too drunk then call a cab. 
  • @AfterReturning - Will be used to Check Normal flow. Give good bye gift to guests who didn't get too drunk.
  • @After - Will be used to handle both normal or Exception conditions. Send a thank you email to all the guest irrespective of whether they were drunk or not.


  • Package Structure:
    Spring AOP Sample code


    Lets start with creating a PartyService with letsParty() method to show the use of advices.
     package com.blogspot.javasampleprogram.service;  
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     public interface IPartyService {  
          public void letsParty(PartyPeople people) throws Exception;  
     }  
    
    PartyService Implementation class
     package com.blogspot.javasampleprogram.service;  
     import com.blogspot.javasampleprogram.exception.GotTooDrunkException;  
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     public class PartyServiceImpl implements IPartyService {  
          /**  
           * guests wait for some time in party then leave.  
           * If a guest gets too drunk then they are taken out.  
           */  
          public void letsParty(PartyPeople people) throws Exception {  
                    Thread.sleep(200);  
                    // if people get drunk then get them out
                    if (people.isDrunk()) {  
                         throw new GotTooDrunkException();  
                    }  
          }  
     }  
    

    Data transfer object that stores the information of people coming in.
     package com.blogspot.javasampleprogram.service;  
     import java.io.Serializable;  
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     public class PartyPeople implements Serializable {  
          public PartyPeople(){};  
          
          public PartyPeople(String name){this.name = name;};  
          
          public PartyPeople(String name, boolean isDrunk) {
            this.name = name; 
            this.drunk = isDrunk;
          };  
          public String name;  
          public boolean drunk;  
          
         /**  
           * @return the drunk  
           */  
          public boolean isDrunk() {  
               return drunk;  
          }  
          /**  
           * @param drunk the drunk to set  
           */  
          public void setDrunk(boolean drunk) {  
               this.drunk = drunk;  
          }  
          /**  
           * @return the name  
           */  
          public String getName() {  
               return name;  
          }  
          /**  
           * @param name the name to set  
           */  
          public void setName(String name) {  
               this.name = name;  
          }  
     }  
    
    Exception thrown when the guest is not in guestlist
     package com.blogspot.javasampleprogram.exception;  
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     public class NotOnListException extends Exception {  
          public NotOnListException() {  
               super();  
          }  
          public NotOnListException(String message) {  
               super(message);  
          }  
     }  
    
    Exception thrown when the guest becomes too drunk
     package com.blogspot.javasampleprogram.exception;  
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     public class GotTooDrunkException extends Exception {  
          public GotTooDrunkException() {  
               super();  
          }  
          public GotTooDrunkException(String message) {  
               super(message);  
          }  
     }  
    

    Spring AOP advices :

    1. Before Advice 

    Before advice will authenticate guests. Only people in guest list can come.
     package com.blogspot.javasampleprogram.aspect;  
    
     import org.aspectj.lang.JoinPoint;  
     import org.aspectj.lang.annotation.Aspect;  
     import org.aspectj.lang.annotation.Before;  
     import com.blogspot.javasampleprogram.exception.NotOnListException;  
     import com.blogspot.javasampleprogram.service.PartyPeople;  
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     @Aspect  
     public class BeforePartyAspect {  
          /**  
           * Advice to log entering and exit of guests.  
           * @param proceedingJoinPoint  
           */  
          @Before(value="(execution(* com.blogspot.javasampleprogram.service.*.*(..)))")  
          public void checkGuestList(JoinPoint joinPoint) throws NotOnListException {  
               // get method arguments   
               Object[] args = joinPoint.getArgs();  
               
               // getting the method argument using Joinpoint API  
               PartyPeople partyPeople = (PartyPeople)args[0];  
               
               boolean onGuestList = false;  
               // checking guest list  
               for (int i = 0; i < partyPeoples.length; i++) {  
                    if (partyPeople.getName().equals(partyPeoples[i].getName())) {  
                         onGuestList = true;  
                         break;  
                    }  
               }  
               
               if (!onGuestList) {  
                    throw new NotOnListException(partyPeople.getName()+" trying to gatecrash.");  
               }  
          }  
    
          // guest list
          PartyPeople[] partyPeoples = {new PartyPeople("jason statham"),  
                                              new PartyPeople("john travolta"),            
                                              new PartyPeople("arnold"),            
                                              new PartyPeople("christian bale"),            
                                              new PartyPeople("Vin Diesel")  
          };  
     }  
    
    Spring application context file appContext.xml 
     <?xml version="1.0" encoding="UTF-8" standalone="no"?>  
     <beans xmlns="http://www.springframework.org/schema/beans"  
          xmlns:aop="http://www.springframework.org/schema/aop"  
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
          xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
               http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
         
         <!-- The @AspectJ support is enabled by including the below tag -->     
         <aop:aspectj-autoproxy/>  
         
         <bean id="partyService"   
           class="com.blogspot.javasampleprogram.service.PartyServiceImpl" />  
         
         <!-- Aspect -->  
         <bean id="beforePartyAspect" class="com.blogspot.javasampleprogram.aspect.BeforePartyAspect" />  
     </beans>  
    
    Run PartyTest, 'adam sandler' who is not on the list will be caught by the before advice.
     package com.blogspot.javasampleprogram.test;
      
     import org.springframework.context.ApplicationContext;  
     import org.springframework.context.support.ClassPathXmlApplicationContext;  
     import com.blogspot.javasampleprogram.exception.NotOnListException;  
     import com.blogspot.javasampleprogram.service.IPartyService;  
     import com.blogspot.javasampleprogram.service.PartyPeople;  
    
     public class PartyTest {  
    
          public static void main(String[] args) {  
               ApplicationContext context1 = new ClassPathXmlApplicationContext(new String[] { "appContext.xml" });  
    
               PartyPeople guest1 = new PartyPeople("jason statham", false);  
               PartyPeople guest2 = new PartyPeople("adam sandler", true);  
    
               IPartyService partyService = (IPartyService)context1.getBean("partyService");  
    
               // in guest list  
               try {  
                    partyService.letsParty(guest1);  
               } catch (NotOnListException e) {System.out.println(e.getMessage());}  
               catch (Exception e) {}  
    
               System.out.println("--------------------------------------");  
    
               // @before advice throws NotOnListException   
               try {  
                    partyService.letsParty(guest2);  
               } catch (NotOnListException e) {System.out.println(e.getMessage());}  
               catch (Exception e) {}  
    
               System.out.println("--------------------------------------");  
          }  
     }  
    
    Output
     --------------------------------------  
     adam sandler trying to gatecrash.  
     --------------------------------------  
    

    2. Around advice

    Audits the entry and exit time of guests.
     package com.blogspot.javasampleprogram.aspect;  
     
     import java.util.Calendar;  
     import org.aspectj.lang.ProceedingJoinPoint;  
     import org.aspectj.lang.annotation.Around;  
     import org.aspectj.lang.annotation.Aspect;  
     import com.blogspot.javasampleprogram.service.PartyPeople;  
     
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     @Aspect  
     public class AroundPartyAspect {  
          /**  
           * Advice to log entering and exit of guests.  
           * @param proceedingJoinPoint  
           * @throws Throwable   
           */  
          @Around(value="(execution(* com.blogspot.javasampleprogram.service.*.*(..)))")  
          public void audit(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {  
     
               // get method arguments   
               Object[] args = proceedingJoinPoint.getArgs();  
    
               // getting the method argument using Joinpoint API  
               PartyPeople partyPeople = (PartyPeople)args[0];  
               
               //auditing entry and exit  
               System.out.println(partyPeople.getName()+" came in at "+Calendar.getInstance().get(Calendar.HOUR_OF_DAY)+":"+Calendar.getInstance().get(Calendar.MINUTE));  
               
               try {  
                    proceedingJoinPoint.proceed();  
               } finally {  
                    // exit time kept in finally block so that even if there is any exception from method  
                    // the exit time still gets audited  
                    System.out.println(partyPeople.getName()+" left at "+Calendar.getInstance().get(Calendar.HOUR_OF_DAY)+":"+Calendar.getInstance().get(Calendar.MINUTE));  
               }  
          }  
     }  
    
    Spring configuration file
     <?xml version="1.0" encoding="UTF-8" standalone="no"?>  
     <beans xmlns="http://www.springframework.org/schema/beans"  
          xmlns:aop="http://www.springframework.org/schema/aop"  
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
          xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
               http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
        
         <!-- The @AspectJ support is enabled by including the below tag -->     
         <aop:aspectj-autoproxy/>  
        
         <bean id="partyService"   
           class="com.blogspot.javasampleprogram.service.PartyServiceImpl" />  
         
         <!-- Aspect -->  
         <bean id="aroundPartyAspect" class="com.blogspot.javasampleprogram.aspect.AroundPartyAspect" />  
     </beans>  
    

    Run the PartyTest, the entry and exit time of each guest is audited.
     package com.blogspot.javasampleprogram.test;  
     
     import org.springframework.context.ApplicationContext;  
     import org.springframework.context.support.ClassPathXmlApplicationContext;  
     import com.blogspot.javasampleprogram.exception.NotOnListException;  
     import com.blogspot.javasampleprogram.service.IPartyService;  
     import com.blogspot.javasampleprogram.service.PartyPeople;  
    
     public class PartyTest {  
     
         public static void main(String[] args) {  
    
               ApplicationContext context1 = new ClassPathXmlApplicationContext(new String[] { "appContext.xml" });  
    
               PartyPeople guest1 = new PartyPeople("jason statham", false);  
               PartyPeople guest2 = new PartyPeople("john travolta", true);  
    
               System.out.println("--------------------------------------");  
               IPartyService partyService = (IPartyService)context1.getBean("partyService");  
    
               // in guest list and not drunk  
               try {  
                    partyService.letsParty(guest1);  
               } catch (NotOnListException e) {System.out.println(e.getMessage());}  
               catch (Exception e) {}  
               System.out.println("--------------------------------------");  
    
               // in guest list and drunk. Even if exception is thrown still the exit time is audited  
               try {  
                    partyService.letsParty(guest2);  
               } catch (NotOnListException e) {System.out.println(e.getMessage());}  
               catch (Exception e) {}  
               System.out.println("--------------------------------------");  
          }  
     }  
    

     --------------------------------------  
     jason statham came in at 10:25  
     jason statham left at 10:25  
     --------------------------------------  
     john travolta came in at 10:25  
     john travolta left at 10:25  
     --------------------------------------  
    

    3. AfterThrowing advice

    Handle the GotTooDrunkException and calls cab for the drunk guests.
     package com.blogspot.javasampleprogram.aspect;  
     
     import org.aspectj.lang.JoinPoint;  
     import org.aspectj.lang.annotation.AfterThrowing;  
     import org.aspectj.lang.annotation.Aspect;  
     import com.blogspot.javasampleprogram.exception.GotTooDrunkException;  
     import com.blogspot.javasampleprogram.service.PartyPeople;  
     
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     @Aspect  
     public class AfterThrowingPartyAspect {  
     
         /**  
           * Advice to send thank You Email to all guests, irrespective of whether they were   
           * too drunk or not.  
           * @param joinPoint  
           */  
          @AfterThrowing(value="(execution(* com.blogspot.javasampleprogram.service.*.*(..)))", throwing="exception")  
          public void callCabForDrunkGuests(JoinPoint joinPoint, Exception exception) {  
    
               if (exception instanceof GotTooDrunkException) {  
                         // get method arguments   
                         Object[] args = joinPoint.getArgs();  
    
                         // getting the method argument using Joinpoint API  
                         PartyPeople partyPeople = (PartyPeople)args[0];  
                         System.out.println(partyPeople.getName()+" got too drunk. Calling cab!!");  
               }   
               // No need to handle NotOnListException as exceptions thrown by @Before advice never come to @AfterThrowing  
          }  
     }  
    

    Spring configuration file
     <?xml version="1.0" encoding="UTF-8" standalone="no"?>   
      <beans xmlns="http://www.springframework.org/schema/beans"   
        xmlns:aop="http://www.springframework.org/schema/aop"   
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">   
    
        <!-- The @AspectJ support is enabled by including the below tag -->     
        <aop:aspectj-autoproxy/>   
    
        <bean id="partyService"    
         class="com.blogspot.javasampleprogram.service.PartyServiceImpl" />   
    
        <!-- Aspect -->   
        <bean id="afterThrowingPartyAspect" class="com.blogspot.javasampleprogram.aspect.AfterThrowingPartyAspect" />   
      </beans>   
    

    Run the above test again. Called cabs for guests, who got too drunk.
     --------------------------------------  
     john travolta got too drunk. Calling cab!!  
     --------------------------------------  
    

    4. AfterReturning Advice

    If the guest is not drunk then no exception will be thrown from method and afterreturning advice will give gifts to these guests.
     package com.blogspot.javasampleprogram.aspect;  
     
     import org.aspectj.lang.JoinPoint;  
     import org.aspectj.lang.annotation.AfterReturning;  
     import org.aspectj.lang.annotation.Aspect;  
     import com.blogspot.javasampleprogram.service.PartyPeople;  
     
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     @Aspect  
     public class AfterReturningPartyAspect {  
          /**  
           * Advice to give party gift to guests who came out without getting too drunk.  
           * @param joinPoint  
           */  
          @AfterReturning(value="(execution(* com.blogspot.javasampleprogram.service.*.*(..)))")  
          public void givePartyGift(JoinPoint joinPoint) {  
               // get method arguments   
               Object[] args = joinPoint.getArgs();  
              
               // getting the method argument using Joinpoint API  
               PartyPeople partyPeople = (PartyPeople)args[0];  
              
               System.out.println(partyPeople.getName()+" got party gift.");  
          }  
     }  
    

    Spring configuration file
     <?xml version="1.0" encoding="UTF-8" standalone="no"?>   
      <beans xmlns="http://www.springframework.org/schema/beans"   
        xmlns:aop="http://www.springframework.org/schema/aop"   
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">   
    
        <!-- The @AspectJ support is enabled by including the below tag -->     
        <aop:aspectj-autoproxy/>   
    
        <bean id="partyService"    
         class="com.blogspot.javasampleprogram.service.PartyServiceImpl" />   
    
        <!-- Aspect -->   
        <bean id="afterReturningPartyAspect" class="com.blogspot.javasampleprogram.aspect.AfterReturningPartyAspect" />   
      </beans>   
    

    Run the above test again. Gift given to only those guests which were not drunk or for which the GotTooDrunkException was not thrown.
     --------------------------------------  
     jason statham got party gift.  
     --------------------------------------  
    

    5. After Advice

     package com.blogspot.javasampleprogram.aspect;  
     
     import org.aspectj.lang.JoinPoint;  
     import org.aspectj.lang.annotation.After;  
     import org.aspectj.lang.annotation.Aspect;  
     import com.blogspot.javasampleprogram.service.PartyPeople;  
     /**  
      * @author http://java-sample-program.blogspot.in/  
      */  
     @Aspect  
     public class AfterPartyAspect {  
          /**  
           * Advice to send thank You Email to all guests, irrespective of whether they were   
           * too drunk or not.  
           * @param joinPoint  
           */  
          @After(value="(execution(* com.blogspot.javasampleprogram.service.*.*(..)))")  
          public void sendThankYouEmail(JoinPoint joinPoint) {  
     
              // get method arguments   
              Object[] args = joinPoint.getArgs();  
    
              // getting the method argument using Joinpoint API  
              PartyPeople partyPeople = (PartyPeople)args[0];  
              
              System.out.println("Sent thank you email to "+partyPeople.getName()+". Thank you "+partyPeople.getName()+" for coming to "+joinPoint.getSignature().getName());  
          }  
     }  
    
    Spring configuration file
     <?xml version="1.0" encoding="UTF-8" standalone="no"?>   
      <beans xmlns="http://www.springframework.org/schema/beans"   
        xmlns:aop="http://www.springframework.org/schema/aop"   
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
        xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">   
    
        <!-- The @AspectJ support is enabled by including the below tag -->     
        <aop:aspectj-autoproxy/>   
    
        <bean id="partyService"    
         class="com.blogspot.javasampleprogram.service.PartyServiceImpl" />   
    
        <!-- Aspect -->   
        <bean id="afterPartyAspect" class="com.blogspot.javasampleprogram.aspect.AfterPartyAspect" />   
      </beans>   
    
    Run the test again. Every guest (drunk or not drunk) was sent a thank you email.
     --------------------------------------  
     Sent thank you email to jason statham. Thank you jason statham for coming to letsParty  
     --------------------------------------  
     Sent thank you email to john travolta. Thank you john travolta for coming to letsParty  
     --------------------------------------  
    

    My next blog will apply all these advices and in proper order using the @Order annotation.


    Jar files used:
    1. aspectjrt-1.5.3.jar
    2. org.springframework.context-3.0.4.RELEASE.jar
    3. org.springframework.beans-3.0.3.RELEASE.jar
    4. org.springframework.core-3.0.3.RELEASE.jar
    5. org.springframework.asm-3.0.3.RELEASE.jar
    6. org.springframework.expression-3.0.3.RELEASE.jar
    7. org.springframework.aop-3.0.3.RELEASE.jar
    8. com.springsource.org.aopalliance-1.0.0.jar
    9. aspectjweaver-1.6.8.jar


    3 comments:

    /* */