Sunday, September 29, 2013

Spring AOP - Part II

For Part -I, refer blog: Spring AOP-Part I

Let me give a simple example of AOP to add logging service.

Pom.xml

<dependency> 
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version> 
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.11</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.6.8</version>
</dependency>

<!--  logging -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.5</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.16</version>
    <scope>runtime</scope>
</dependency>

AOP Targets:

These are the interfaces/classes on which AOP will be applied. We will define a simple tax calculation service.
public interface TaxService {
        public double calculate(String state,String price);

}

public class TaxServiceImpl implements TaxService{
         public double calculate(String state, String price) {
                double tax = 0.0;
                double priceDbl = Double.parseDouble(price);
    
                if("NY".equals(state)){
                       tax =priceDbl +  priceDbl *0.25;
                }
                else{
                       tax =priceDbl +  priceDbl *0.20;
                }
  
                return tax;
  
         }

Aspect classes:

These are the interface/classes where you will implement the cross cutting concern. In our case, logging. Note that we will get the JoinPoint instance, from which we can derive the method arguments being passed to the target methods.Logging will be  applied before entering a method, after method exit as well in case of any exception being thrown.
public interface LoggingService {
       public void logEntry(JoinPoint jp);
       public void logExit(JoinPoint jp);
       public void logException(JoinPoint jp);
}

public class LoggingServiceImpl implements LoggingService{
 
        private static final Logger logger = LoggerFactory.getLogger(com.myorg.service.LoggingService.class);
 
        public void logEntry(JoinPoint joinPoint){
                logger.debug("Entering method: "+joinPoint + " with args: "+Arrays.toString(joinPoint.getArgs()));
        }
        
        public void logExit(JoinPoint joinPoint){
                logger.debug("Exit method: "+joinPoint);
        }
 
        public void logException(JoinPoint joinPoint){
                logger.debug("In Exception method: "+joinPoint);
        }
}

Spring Config XML

Here we configure the AOP targets and the point cuts and specify the expression for identifying the pointcuts.

<bean id="taxService" class="com.myorg.service.TaxServiceImpl"/>
<bean id="loggingService" class="com.myorg.service.LoggingServiceImpl"/>

<aop:config>
    <aop:aspect ref="loggingService">  
        <aop:pointcut  id ="logMethod" expression="execution(* com.myorg.service.TaxService.*(..))"/>
 
        <aop:before  method="logEntry"  pointcut-ref="logMethod"/>
        <aop:after  method="logExit"  pointcut-ref="logMethod"/>
       <aop:after-throwing  method="logException"  pointcut-ref="logMethod"/>
 
    </aop:aspect>

</aop:config>

log4j properties

Place the below file in classpath to configure the log4j.

# Define the root logger with appender file
log4j.rootLogger = DEBUG, FILE

# Define the file appender
log4j.appender.FILE=org.apache.log4j.FileAppender
# Set the name of the file
log4j.appender.FILE.File=

# Set the immediate flush to true (default)
log4j.appender.FILE.ImmediateFlush=true

# Set the threshold to debug mode
log4j.appender.FILE.Threshold=debug

# Set the append to false, overwrite
log4j.appender.FILE.Append=false

# Define the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%m%n

Testing:

Now that the configurations and code is in place, invoke the TaxService from any of the controllers and you will find the log statements for every method invoked on TaxService. This way, logging is separated out of the actual business logic. 

                                                                                                 Continued......

No comments:

Post a Comment