Spring AOP: After (Finally) Advice

In this article, we will discuss after (finally) advice in detail. This is basically intercepted after the execution of the method/s irrespective of the outcome i.e.; normal or exceptional return

We will implement below 2 approaches

  • Using pointcut expression and registering corresponding class in the spring xml file
  • Using AspectJ Annotations i.e.; (@Aspect / @After)

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 with advices

Note: one of method getAllEmployee() deliberately coded to throw exception to learn that advice is being advised for the target object even in case of exceptional scenario

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() {
		// included below loc to explicitly throw ArithmaticException
		// and verify whether after (finally) advice is applied or not to the target employeeService bean
		int iResult = 1/0;
		System.out.println("getAllEmployee : lists all employee details");
	}
}

Let’s discuss different approaches for after (finally) advice

Approach 1: Using pointcut expression

Create after (finally) advice class & one method to this class for referring from pointcut expression declared in the SpringAOPContext.xml

AfterFinallyAdviceInterceptor.java

package com.spring.series.aop.advice;

public class AfterFinallyAdviceInterceptor {

	/**
	 * this is method is invoked from the pointcut expression declared in the spring context xml
	 */
	public void afterFinallyAdviceUsingPointcutExpression() {
		System.out.println("******** After (Finally) Advice using Pointcut Expression ********");
	}
}

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
  • afterFinallyAdviceInterceptor bean for before advice
  • aspect with reference to afterFinallyAdviceInterceptor for pointcut expression

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" />

	<!-- after Finally advice class -->
	<bean id="afterFinallyAdviceInterceptor" class="com.spring.series.aop.advice.AfterFinallyAdviceInterceptor" />

	<!-- pointcut expression for after Finally advice -->
	<aop:config>
		<aop:aspect id="aspect" ref="afterFinallyAdviceInterceptor">
			<aop:pointcut id="pointcutExpression" expression="execution(* com.spring.series.aop.service.*.get*(..))" />
			<aop:after pointcut-ref="pointcutExpression" method="afterFinallyAdviceUsingPointcutExpression" />
		</aop:aspect>
	</aop:config>
</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)

afterFinallyAdvice

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("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

With below output in the console, it’s self explanatory that after (finally) advice is applied to the method getAllEmployee() even when it throws the ArithamticException for DivideByZero exception

Aug 04, 2014 7:45:32 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@766e119d: startup date [Mon Aug 04 19:45:32 IST 2014]; root of context hierarchy
Aug 03, 2014 7:45:32 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

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

updateEmployee : updates employee details based on the employee ID

deleteEmployee : deletes employee based on the employee ID
******** After (Finally) Advice using Pointcut Expression ********

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.spring.series.aop.service.EmployeeServiceProvider.getAllEmployee(EmployeeServiceProvider.java:30)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at $Proxy3.getAllEmployee(Unknown Source)
	at com.spring.series.aop.TestEmployee.testAdvice(TestEmployee.java:28)
	at com.spring.series.aop.TestEmployee.main(TestEmployee.java:11)

If you see output, after returning advice is applied to methods which are starting with get and taking any number of arguments. We can widen it to apply to all business methods of the employee service by changing get*() to simple * (wild card character) in the spring xml under the tag

<aop:pointcut id=“pointcutExpression” expression=“execution(*  com.spring.series.aop.service.*.get*(..))” />

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

 

Approach 2: Using AspectJ Annotations i.e.; (@Aspect / @After)

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

Create a class annotated with @Aspect and advice methods with @After annotation for after(finally) advice

AfterFinallyAdviceAnnotation.java

package com.spring.series.aop.advice;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AfterFinallyAdviceAnnotation {

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

	@After("execution(* com.spring.series.aop.service.*.get*(..))")
	public void afterFinallyUsingAnnotation() {
		System.out.println("******** After (Finally) Advice using Annotation ********");
	}
}

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
  • afterFinallyAdviceAspect bean for after(finally) advice which is annotated with @Aspect/@After 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 />

	<!-- Aspect : after Finally advice using annotation -->
	<bean id="afterFinallyAdviceAspect" class="com.spring.series.aop.advice.AfterFinallyAdviceAnnotation" />

</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

With below output in the console, it’s self explanatory that after (finally) advice is applied to method getAllEmployee() even when it throws the ArithamticException for DivideByZero exception

Aug 04, 2014 7:43:30 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6c1961f4: startup date [Mon Aug 04 19:43:30 IST 2014]; root of context hierarchy
Aug 04, 2014 7:43:30 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

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

updateEmployee : updates employee details based on the employee ID

deleteEmployee : deletes employee based on the employee ID
******** After (Finally) Advice using Annotation ********

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.spring.series.aop.service.EmployeeServiceProvider.getAllEmployee(EmployeeServiceProvider.java:30)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at $Proxy6.getAllEmployee(Unknown Source)
	at com.spring.series.aop.TestEmployee.testAdvice(TestEmployee.java:28)
	at com.spring.series.aop.TestEmployee.main(TestEmployee.java:11)

This annotation states that method annotated with @After 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 @After 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-After-Finally-Advice-using-Pointcut (4kB)
Spring-AOP-After-Finally-Advice-using-@After-Annotation (4kB)

 

Read Also:

 

Happy Coding !!
Happy Learning !!

Spring AOP: After Throwing Advice
Spring AOP: After Returning Advice