Spring AOP Example
In this example, we shall understand what is Aspect Oriented Programming and how Spring provides support to implement AOP.
1. Introduction to AOP
When designing a module, the focus is usually to maintain high cohesion in the module. Cohesion as per Wikipedia is the degree to which elements of a module belong together. But there are certain cross-cutting concerns like logging, transaction management, authorization etc which are required in almost to all modules. However, they do not serve any business purpose and thus reduce the focus of the module.
Aspect Oriented Programming provides a way to address this cross-cutting without embedding code specific to those tasks in the target module. AOP works by wrapping its code around the target module methods at run-time. Let’s set up the environment and see how Spring can help us in implementing Aspect Oriented Programming.
2. Project Setup
Create a simple Maven Project in Eclipse IDE by selecting the Skip Archetype Selection checkbox from the New Maven Project Pop-up.
We are using the below pom.xml
to manage the dependencies for Spring AOP and AspectJ:
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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>com.jcg.examples.springAoopExample</groupId> <artifactId>SpringAOPExample</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.2.4.RELEASE</version> </dependency> </dependencies> </project>
We shall be using AspectJ styled annotation for defining our point-cuts.
Eclipse will download the required JAR files and add the dependencies in the project classpath. Now that the setup is complete, let’s start with the implementation.
3. Implementation
We are given a business class around which we have to apply Aspect Oriented Programming. Let’s have a look at the business class:
BusinessTargetObject.java
package com.jcg.examples.bo; import org.springframework.stereotype.Component; @Component public class BusinessTargetObject { public String performTransaction(String arg) { System.out.println("Performing Txn for: " +arg); return "Transaction "+arg+" Successful"; } public void merryGoAround() { System.out.println("Running merryGoAround for Business"); } public void sayHello() { System.out.println("Hello says Hello.."); } public void throwException() { System.out.println("Business class about throw an NPE.."); throw new NullPointerException("THrowing custom Exception"); } }
The above class has two methods around which we need to add loggers. The class is annotated with @Component
so that we can have its instance from the Spring container for testing.
Here’s the configuration xml which will configure the Spring Container :
Spring-configuration.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" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <aop:aspectj-autoproxy /> <context:component-scan base-package="com.jcg.examples"/> </beans>
Line 16 tells the container that we are AspectJ
controls for configuration. Spring uses Proxy pattern to create advice around a target object. Now, what is advice?
- Advice : The actual code to be executed around the target for logging, authorization etc.
- Target : The target around which the cross-cutting concern is to be fitted.In our case, it will be the methods in the
BusinessTargetObject
- Join Point : The point in the flow of the target program where the advice needs to be hooked to the target object.
- Point-cut : We can think of point-cut as a sort of regular expression which selects a set of Join Points from the target, where the advice needs to run. As we are using
AspectJ
as our code weaver we are the ApectJ expression language as well.
We configure the point-cut in the Profiler.java
file below:
Profiler.java
package com.jcg.examples.profiler; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; 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.springframework.stereotype.Component; @Aspect @Component public class Profiler { @Before("execution(* com.jcg.examples.bo.BusinessTargetObject.sayHello(..))") public void logBeforeTxn(JoinPoint joinpoint) { System.out.println("Beginning execution for "+joinpoint.getSignature().getName()); } @After("execution(* com.jcg.examples.bo.BusinessTargetObject.sayHello(..))") public void logAfterTxn(JoinPoint joinpoint) { System.out.println("Execution completed for "+joinpoint.getSignature().getName()); } @Around("execution(* com.jcg.examples.bo.BusinessTargetObject.merryGoAround(..))") public void logAroundTxn(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("Beginning execution for "+proceedingJoinPoint.getSignature().getName()); proceedingJoinPoint.proceed(); System.out.println("Execution completed for "+proceedingJoinPoint.getSignature().getName()); } @AfterReturning(pointcut = "execution(* com.jcg.examples.bo.BusinessTargetObject.performTransaction(..))",returning = "retVal") public void logResultsAfterTxn(JoinPoint joinpoint, Object retVal) { System.out.println("Execution completed for "+joinpoint.getSignature().getName()); System.out.println("Value being returned is "+retVal); } @AfterThrowing(pointcut = "execution(* com.jcg.examples.bo.BusinessTargetObject.throwException(..))",throwing = "exception") public void logResultsAfterError(JoinPoint joinpoint, Throwable exception) { System.out.println("Execution completed for "+joinpoint.getSignature().getName()); System.out.println("Error in Join Point due to : "+exception.getMessage()); System.out.println("Advice complete"); } }
The first thing is the @Aspect
annotation. This annotation is used to mark the class as an Aspect. The Spring Container cannot detect the class just by Aspect annotation, so we have marked it as Component for it to be auto-detected.
Next, we have a @Before
annotation which marks the method to run the code before the execution of the target method begins. We pass the AspectJ point-cut expression
as the parameter to the annotation. The advice will run for the methods for which the criteria defined by this point-cut expression is met. The AspectJ expression syntax can be understood in detail from the official page of the AspectJ.
Similarly, @After
runs after the target method has completed running. One major difference between @After
and @AfterReturning
is that the latter also has the value returned by the target method. To access this value the developer has to pass the name of the argument in the annotation.
@Around
This tag combines the functionalities of the @Before
and @After
annotations.
@AfterThrowing
is executed if a join point throws an exception. The annotation allows the developer to access the exception object via its throwing
attribute.
If a method satisfies more than one point cut expressions, the advice with the highest precedence will run first when starting with the execution of the Join point. After the execution of Join point is complete, the advice with the lowest precedence will run first.
We can set the order of the advises either by implementing the Ordered
interface or by using the @Order
annotation. for e.g.:
@Order(value=1) @After("execution(* com.jcg.examples.bo.BusinessTargetObject.sayHello(..))") public void logAfterTxn(JoinPoint joinpoint) { System.out.println("Execution completed for "+joinpoint.getSignature().getName()); }
The advice with the lowest order value has the highest precedence.
Now, we are done with the implementation. Let’s test our code.
TestLoggingAspect.java
package com.jcg.examples.test; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import com.jcg.examples.bo.BusinessTargetObject; public class TestLoggingAspect { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new ClassPathResource("spring-configuration.xml").getPath()); BusinessTargetObject target = context.getBean(BusinessTargetObject.class); target.sayHello(); target.performTransaction("JavaCodeGeeks"); target.merryGoAround(); target.throwException(); context.close(); } }
In the above class, we have retrieved the instance of BusinessTargetObject
and executed its method. The advices
from the Profiler
class run as and when their pointcut expressions are satisfied
Here’s how the output looks like:
Beginning execution for sayHello Hello says Hello.. Execution completed for sayHello Performing Txn for: JavaCodeGeeks Execution completed for performTransaction Value being returned is Transaction JavaCodeGeeks Successful Beginning execution for merryGoAround Running merryGoAround for Business Execution completed for merryGoAround Business class about throw an NPE.. Execution completed for throwException Error in Join Point due to : THrowing custom Exception Advice complete
4. Download the Source Code
Here we understood what Aspect Oriented Programming is and how we can use it to improve cohesion in our modules using Spring.
You can download the source code of this example here: SpringAOPExample.zip