Cross Cutting Concerns
A cross cutting concern is a concern that is spread over multiple modules. For example logging user activities whenever any operation is performed is a cross cutting concern. To understand this lets say we have a service layer in our application and there are different services on that layer. If we want to log user activity whenever any operation is performed then we would definitely need to embed same logging code into each service class which is obviously code scattering.
The above issue is tackled by AOP (Aspect Oriented Programming) which removes the need for writing redundant code in our application.
AOP (Aspect Oriented Programming)
As the name suggests AOP is based on Aspects. Each Aspect is a module that removes the need to scatter similar code over multiple modules thus AOP modularizes the cross cutting concerns.
Core Concepts
Joinpoint: The point in the execution of an application where you want insert additional logic to be executed.
Pointcut: Expression that select one or more jointpoints.
Advice: Code that will be executed at a jointpoint that has been selected by a pointcut. Following are the types of Advice.
1. Before Advice (this executes before joinpoint)
2. After Advice (this executes after joinpoint)
3. Around Advice (this executes around joinpoint)
2. After Advice (this executes after joinpoint)
3. Around Advice (this executes around joinpoint)
Aspect: Module that encapsulate pointcuts and advices.
Spring AspectJ Example
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelversion>4.0.0</modelversion> <groupid>org.atif.demo.aop</groupid> <artifactid>aop</artifactid> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>aop</name> <url>http://maven.apache.org</url> <properties> <project .build.sourceencoding="">UTF-8</project> </properties> <dependencies> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> <version>2.5</version> </dependency> <dependency> <groupid>aspectj</groupid> <artifactid>aspectjrt</artifactid> <version>1.5.4</version> </dependency> <dependency> <groupid>cglib</groupid> <artifactid>cglib</artifactid> <version>2.2</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-aop</artifactid> <version>2.5</version> </dependency> <dependency> <groupid>org.aspectj</groupid> <artifactid>aspectjweaver</artifactid> <version>1.6.11</version> </dependency> <dependency> <groupid>commons-logging</groupid> <artifactid>commons-logging</artifactid> <version>1.1.3</version> </dependency> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>Operation.java
package org.atif.demo.aop; /** * * @author atif */ public enum Operation { ADD("Addition"), EDIT("Update"), DELETE("Delete"), VIEW("View"); private String name = null; Operation(String name) { this.name = name; } public String getName() { return this.name; } }EventLogger.java
package org.atif.demo.aop; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author atif * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface EventLogger {Operation op(); }EventLoggerAspect.java
package org.atif.demo.aop; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.core.Ordered; /** * Simple Spring AOP aspect, intercepting every method * which uses the {@link EventLoggerAspect} annotation. * * Every method willing to have access checked, needs to declare * both the annotation and {@link EventLoggerAspect} as one of its parameters. * * @author atif */ @Aspect public class EventLoggerAspect implements Ordered { private static Log LOG = LogFactory.getLog(EventLoggerAspect.class); private int order = 0; /** * Logs user operation. * An AspectJ annotation declares the pointcut for this aspect. * * @param joinPoint AOP object which will get automatically injected * @param op The EventLogger annotation as extracted from the intercepted method. */ @Before("@annotation(logger)") public void logOperationStart(JoinPoint joinPoint, EventLogger logger) { Operation operation = logger.op(); System.out.println("About to perform " + operation.getName() + " operation."); } /** * Logs user operation. * An AspectJ annotation declares the pointcut for this aspect. * * @param joinPoint AOP object which will get automatically injected * @param op The EventLogger annotation as extracted from the intercepted method. */ @After("@annotation(logger)") public void logOperationEnd(JoinPoint joinPoint, EventLogger logger) { Operation operation = logger.op(); System.out.println("Performed " + operation.getName()+ " operation."); } public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return order; } }CRUDService.java
package org.atif.demo.aop; /** * * @author atif */ public class CRUDService { @EventLogger(op= Operation.ADD) public void add() { System.out.println("Operation in progress..."); } @EventLogger(op= Operation.EDIT) public void edit() { System.out.println("Operation in progress..."); } @EventLogger(op= Operation.DELETE) public void delete() { System.out.println("Operation in progress..."); } @EventLogger(op= Operation.VIEW) public void select() { System.out.println("Operation in progress..."); } }applicationContext.xml
<beans xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <aop:aspectj-autoproxy> </aop:aspectj-autoproxy> <context:annotation-config/> <context:component-scan base-package="org.atif.demo.aop"/> <bean class="org.atif.demo.aop.EventLoggerAspect" id="eventLoggerAspect"> <property name="order" value="0"> </property> </bean> <bean class="org.atif.demo.aop.CRUDService" id="crudService"> </bean> </beans>
App.java
package org.atif.demo.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * AOP Demo * */ public class App { public static void main( String[] args ) { ApplicationContext applicationContext; applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml"); CRUDService crudService = (CRUDService) applicationContext.getBean("crudService"); crudService.add(); crudService.edit(); crudService.delete(); crudService.select(); } }Output
.....
About to perform Addition operation.
Operation in progress...
Performed Addition operation.
About to perform Update operation.
Operation in progress...
Performed Update operation.
About to perform Delete operation.
Operation in progress...
Performed Delete operation.
About to perform View operation.
Operation in progress...
Performed View operation.
BUILD SUCCESSFUL (total time: 2 seconds)