Spring AOP: Around Advice

In this article, we will discuss around advice in detail. This is kind of combination all three advices we learnt so far. This is basically intercepted before & after the execution of the method/s and throws advice is also applied for exception scenario

We will implement three different approaches

  • Implements MethodInterceptor interface
  • Using pointcut expression implementing MethodInterceptor interface in Spring xml
  • Using AspectJ Annotations i.e.; (@Aspect / @Around)

Note: three approaches covered below are all HAPPY scenarios, but implementation is capable to advice even in case of method/s throws an exception i.e.; similar to ThrowsAdvice

Technology Used

  • Java 1.7
  • Eclipse Kepler IDE
  • Maven 3.0.4
  • Spring-4.0.0-RELEASE
  • AspectJ

Mavenize or download required jars

Add Spring-4.0.0 dependencies to the pom.xml

	<dependencies>
		<!-- Spring dependencies -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- AspectJ dependencies -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj.version}</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${org.aspectj.version}</version>
		</dependency>
	</dependencies>

Folks who aren’t familiar with Maven concepts or don’t require maven for their project, can download the below jars individually from the spring site and include them in the classpath

  • spring-core-4.0.0-RELEASE
  • spring-context-4.0.0-RELEASE
  • spring-beans-4.0.0-RELEASE
  • spring-aop-4.0.0-RELEASE
  • spring-expression-4.0.0-RELEASE
  • commons-logging-1.1.1
  • aopalliance-1.0
  • aspectjrt-1.7.4
  • aspectjweaver-1.7.4

Let’s see coding in action

Create interface & classes for employee with their business methods to be exposed

Employee Service interface

Interface with five methods with simple CRUD like operations for employee bean. All methods inside employee interface are self explanatory

IEmployeeService.java

package com.spring.series.aop.service;

public interface IEmployeeService {

	// simple CRUD operations for employee service
	public int createEmployee(String employeeDetails);
	public void getEmployee(int employeeId);
	public void updateEmployee(int employeeId);
	public void deleteEmployee(int employeeId);
	public void getAllEmployee();
}

Employee Service Provider class

Implementation of the above employee interface with standard sysout’s to print & study the flow of execution

EmployeeServiceProvider.java

package com.spring.series.aop.service;

public class EmployeeServiceProvider implements IEmployeeService {

	@Override
	public int createEmployee(String employeeDetails) {
		System.out.println("createEmployee : adds new employee and returns unique employee ID");
		return 0001;
	}

	@Override
	public void getEmployee(int employeeId) {
		System.out.println("getEmployee : list employee details based on the employee ID");
	}

	@Override
	public void updateEmployee(int employeeId) {
		System.out.println("updateEmployee : updates employee details based on the employee ID");
	}

	@Override
	public void deleteEmployee(int employeeId) {
		System.out.println("deleteEmployee : deletes employee based on the employee ID");
	}

	@Override
	public void getAllEmployee() {
		System.out.println("getAllEmployee() : lists all employee details");
	}
}

Let’s discuss different approaches for before advice

Approach 1: creating advice class implementing standard available interfaces in Spring framework

Create around advice class implementing org.aopalliance.intercept.MethodInterceptor and one more method added to this class pointcut expression

AroundAdviceInterceptor.java

package com.spring.series.aop.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;

public class AroundAdviceInterceptor implements MethodInterceptor {

	Object  object = null;

	@Override
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		System.out.println("******** MethodInterceptor (Before execution) : " + methodInvocation.getMethod().getName() + " ********");
		try{
			object = methodInvocation.proceed();
			System.out.println("******** MethodInterceptor (After execution) : " + methodInvocation.getMethod().getName() + " ********\n");
		}
		catch(Exception ex){
			System.out.println("Exception in Around Advice : " + methodInvocation.getMethod().getName() + " ********");
			ex.printStackTrace();
		}
		return object;
	}

	public void aroundAdviceUsingPointcutExpression(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("******** (Before execution) Around Advice using Pointcut Expression : " + proceedingJoinPoint.getSignature().getName() + " ********");
		try{
			proceedingJoinPoint.proceed();
			System.out.println("******** (After execution) Around Advice using Pointcut Expression : " + proceedingJoinPoint.getSignature().getName() + " ********\n");
		}
		catch(Exception ex){
			System.out.println("Exception in Around Advice using Pointcut Expression : " + proceedingJoinPoint.getSignature().getName() + " ********");
			ex.printStackTrace();
		}
	}
}

Create Spring context root xml to declare beans

Three beans declared in the Spring configuration xml file

  • employeeService bean for the target object for which advice needs to be applied
  • aroundAdviceInterceptor bean for around advice
  • employeeServiceProxy bean combining interceptors & target object for AOP proxy mechanism

SpringAOPContext.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

	<!-- target business class -->
	<bean id="employeeService" class="com.spring.series.aop.service.EmployeeServiceProvider" />

	<!-- around advice class -->
	<bean id="aroundAdviceInterceptor" class="com.spring.series.aop.advice.AroundAdviceInterceptor" />

	<!-- employee proxy -->
	<bean id="employeeServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="proxyInterfaces">
			<list>
				<value>com.spring.series.aop.service.IEmployeeService</value>
			</list>
		</property>
		<property name="interceptorNames">
			<list>
				<value>aroundAdviceInterceptor</value>
			</list>
		</property>
		<property name="target">
			<ref bean="employeeService" />
		</property>
	</bean>
</beans>

Note: Name of the Spring Bean Configuration file can be anything (not necessary to have SpringAOPContext.xml) and it’s your choice. But, in the enterprise application keep these file names appropriate to the business context. So that it will increase the readability of the application.

Project Structure in Eclipse (Package Explorer view)

aroundAdvice

Test the Application that’s exactly …. Run it!

TestEmployee.java

package com.spring.series.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.series.aop.service.IEmployeeService;

public class TestEmployee {

	public static void main(String[] args) {
		testAdvice();
	}

	// test using ApplicationContext
	private static void testAdvice(){

		// load the spring xml configuration file from classpath
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/spring/series/aop/SpringAOPContext.xml");

		// get bean using applicationContext
		IEmployeeService employeeService = (IEmployeeService) applicationContext.getBean("employeeServiceProxy"); // employeeServiceProxy/employeeService

		// invoke business methods of EmployeeService
		employeeService.createEmployee("dummy emp details");
		employeeService.getEmployee(001);
		employeeService.updateEmployee(002);
		employeeService.deleteEmployee(003);
		employeeService.getAllEmployee();
	}
}

Note: getBean() method takes proxy bean as argument and not the target object

Output in console

Aug 04, 2014 10:13:18 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@34469729: startup date [Mon Aug 04 22:13:17 IST 2014]; root of context hierarchy
Aug 04, 2014 10:13:18 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [com/spring/series/aop/SpringAOPContext.xml]

******** MethodInterceptor (Before execution) : createEmployee ********
createEmployee() : adds new employee and returns unique employee ID

******** MethodInterceptor (After execution) : createEmployee ********

******** MethodInterceptor (Before execution) : getEmployee ********
getEmployee() : list employee details based on the employee ID
******** MethodInterceptor (After execution) : getEmployee ********

******** MethodInterceptor (Before execution) : updateEmployee ********
updateEmployee() : updates employee details based on the employee ID

******** MethodInterceptor (After execution) : updateEmployee ********

******** MethodInterceptor (Before execution) : deleteEmployee ********
deleteEmployee() : deletes employee based on the employee ID

******** MethodInterceptor (After execution) : deleteEmployee ********

******** MethodInterceptor (Before execution) : getAllEmployee ********
getAllEmployee() : lists all employee details
******** MethodInterceptor (After execution) : getAllEmployee ********

This way advice is applied to all business methods of the Employee Service, but what if we want to restrict the advice should be advised to some of the particular business methods with around advice

Solution: Include <aop:config> tag with pointcut expression defining the regex in the Spring root configuration xml file

 

Approach 2: Using pointcut expression to limit the advice to be applied to few particular methods

Updated Spring Configuration file with <aop:config> tag

Pointcut expression: “execution(* com.spring.series.aop.service.*.get(..)”

This pointcut expression narrows the advice to be applied only to the execution of above methods

General syntax: execution(<return_type> <qualified.package.name>.<className>.<methodName>.(<arguments>)

So, below pointcut expression narrows only methods whose methodName starts with get and has any number of arguments inside any class but within package com.spring.series.aop.service

SpringAOPContext.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

	<!-- target business class -->
	<bean id="employeeService" class="com.spring.series.aop.service.EmployeeServiceProvider" />

	<!-- around advice class -->
	<bean id="aroundAdviceInterceptor" class="com.spring.series.aop.advice.AroundAdviceInterceptor" />

	<!-- pointcut expression -->
	<aop:config>
		<aop:aspect id="aspect" ref="aroundAdviceInterceptor">
			<aop:pointcut id="pointcutExpression" expression="execution(* com.spring.series.aop.service.*.get*(..))" />
			<aop:around pointcut-ref="pointcutExpression" method="aroundAdviceUsingPointcutExpression" />
		</aop:aspect>
	</aop:config>
</beans>

Let re-run the test class with getBean() method taking target object as argument

TestEmployee.java

package com.spring.series.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.series.aop.service.IEmployeeService;

public class TestEmployee {

	public static void main(String[] args) {
		testAdvice();
	}

	// test using ApplicationContext
	private static void testAdvice(){

		// load the spring xml configuration file from classpath
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/spring/series/aop/SpringAOPContext.xml");

		// get bean using applicationContext
		IEmployeeService employeeService = (IEmployeeService) applicationContext.getBean("employeeService"); // employeeServiceProxy/employeeService

		// invoke business methods of EmployeeService
		employeeService.createEmployee("dummy emp details");
		employeeService.getEmployee(001);
		employeeService.updateEmployee(002);
		employeeService.deleteEmployee(003);
		employeeService.getAllEmployee();
	}
}

Output in console

Aug 04, 2014 10:14:58 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6c1961f4: startup date [Mon Aug 04 22:14:58 IST 2014]; root of context hierarchy
Aug 04, 2014 10:14:58 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [com/spring/series/aop/SpringAOPContext.xml]

createEmployee() : adds new employee and returns unique employee ID

******** (Before execution) Around Advice using Pointcut Expression : getEmployee ********
getEmployee() : list employee details based on the employee ID
******** (After execution) Around Advice using Pointcut Expression : getEmployee ********

updateEmployee() : updates employee details based on the employee ID

deleteEmployee() : deletes employee based on the employee ID

******** (Before execution) Around Advice using Pointcut Expression : getAllEmployee ********
getAllEmployee() : lists all employee details
******** (After execution) Around Advice using Pointcut Expression : getAllEmployee ********</pre>
<pre>

From the output (in console), it is quite clear that only few methods which starts with get*() are applied by the around advice whereas other methods left alone

That’s not all; let’s move onto the next approach

 

Approach 3: Using AspectJ Annotations i.e.; (@Aspect / @Around)

Business interface and their implementation classes remain the same. Only difference with this approach is that, removing those few lines of configuration from spring xml file and replacing with simple AspectJ annotations

Create a class annotated with @Aspect and advice methods with @Around annotation for around advice

AroundAdviceAnnotation.java

package com.spring.series.aop.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AroundAdviceAnnotation {

	// execution(returnType qualified.package.name.classname.methodname(arguments))

	@Around("execution(* com.spring.series.aop.service.*.get*(..))")
	public void aroundAdviceUsingAnnotation(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("******** (Before execution) Around Advice using Annotation : " + proceedingJoinPoint.getSignature().getName() + " ********");
		try{
			proceedingJoinPoint.proceed();
			System.out.println("******** (After execution) Around Advice using Annotation : " + proceedingJoinPoint.getSignature().getName() + " ********\n");
		}
		catch(Exception ex){
			System.out.println("Exception in Around Advice using Annotation : " + proceedingJoinPoint.getSignature().getName() + " ********");
			ex.printStackTrace();
		}
	}
}

Note

  • Now advice will be applied only to the methods whose starting name is get,
  • If we replace this “get*” with just wild card character (*) then advice will be applied to all methods of employee service class

Create Spring context root xml to declare beans and turn on AspectJ annotation

Three beans declared in the Spring configuration xml file

  • employeeService bean for the target object for which advice needs to be applied
  • aroundAdviceAspect bean for around advice which is annotated with @Aspect/@Around annotation
  • <aop:aspectj-autoproxy />  to turn ON AspectJ annotation and automatically creating proxies

SpringAOPContext.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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

	<!-- target business class -->
	<bean id="employeeService" class="com.spring.series.aop.service.EmployeeServiceProvider" />

	<!-- to turn aspectJ annotation -->
	<aop:aspectj-autoproxy />

	<!-- before advice using annotation -->
	<bean id="aroundAdviceAspect" class="com.spring.series.aop.advice.AroundAdviceAnnotation" />

</beans>

Time to test the annotated application example

TestEmployee.java

package com.spring.series.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.series.aop.service.IEmployeeService;

public class TestEmployee {

	public static void main(String[] args) {
		testAdvice();
	}

	// test using ApplicationContext
	private static void testAdvice(){

		// load the spring xml configuration file from classpath
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("com/spring/series/aop/SpringAOPContext.xml");

		// get bean using applicationContext
		IEmployeeService employeeService = (IEmployeeService) applicationContext.getBean("employeeService"); // employeeServiceProxy/employeeService

		// invoke business methods of EmployeeService
		employeeService.createEmployee("dummy emp details");
		employeeService.getEmployee(001);
		employeeService.updateEmployee(002);
		employeeService.deleteEmployee(003);
		employeeService.getAllEmployee();
	}
}

Output in console

Aug 04, 2014 10:22:37 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@766e119d: startup date [Mon Aug 04 22:22:37 IST 2014]; root of context hierarchy
Aug 04, 2014 10:22:37 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [com/spring/series/aop/SpringAOPContext.xml]
createEmployee() : adds new employee and returns unique employee ID

******** (Before execution) Around Advice using Annotation : getEmployee ********
getEmployee() : list employee details based on the employee ID
******** (After execution) Around Advice using Annotation : getEmployee ********

updateEmployee() : updates employee details based on the employee ID

deleteEmployee() : deletes employee based on the employee ID

******** (Before execution) Around Advice using Annotation : getAllEmployee ********
getAllEmployee() : lists all employee details
******** (After execution) Around Advice using Annotation : getAllEmployee ********

This annotation states that advice method annotated with @Around annotation is advised only to the methods whose starting letters are get*(..) inside any class taking any number of arguments but restricted within package com.spring.series.aop.service as attribute in the @Around annotation

Note: to apply advice to all business methods of the employee service class using AspectJ annotation, remove the restriction with just a simple wildcard character (*) à this way advice is applied to all methods of the employee service class

Download project

Spring-AOP-Around-Advice (4kB)
Spring-AOP-Around-Advice-using-Pointcut (4kB)
Spring-AOP-Around-Advice-using-@Around-Annotation (4kB)

 

Read Also:

 

Happy Coding !!
Happy Learning !!

Spring AOP: After Throwing Advice