In this article, we will discuss after throwing advice in detail. This is basically intercepted when exception is thrown from execution of method/s
We will implement three different approaches
- Implements ThrowsAdvice interface
- Using pointcut expression (XML Configuration)
- Using AspectJ Annotations i.e.; (@Aspect / @AfterThrowing)
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.
Note: one of method getAllEmployee() deliberately coded to throw exception to learn that after throwing advice is applied/intercepted for exception 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 throwing advice
Approach 1: creating advice class implementing standard available interfaces in Spring framework
Create after throwing advice class implementing org.springframework.aop.ThrowsAdvice and one more method added to this class for pointcut expression
AfterThrowingAdviceInterceptor.java
package com.spring.series.aop.advice; import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; public class AfterThrowingAdviceInterceptor implements ThrowsAdvice { /** * one of the way to specify method for afterThrowing advice * * @param illegalArgumentException * @throws Throwable */ public void afterThrowing(IllegalArgumentException illegalArgumentException) throws Throwable { System.out.println("******** AfterThrowingAdvice ********"); } /** * other way to specify method for afterThrowing advice & more matured * * @param method * @param args * @param target * @param exception */ public void afterThrowing(Method method, Object args[], Object target, Exception exception) { System.out.println("******** AfterThrowingAdvice : " + method.getName() + " ********"); } /** * poincut expression * * this is method is invoked from the pointcut expression declared in the spring context xml */ public void afterThrowingAdviceUsingPointcutExpression() { System.out.println("******** After Throwing 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
- afterThrowingAdviceInterceptor bean for after throwing 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" /> <!-- after Finally advice class --> <bean id="afterThrowingAdviceInterceptor" class="com.spring.series.aop.advice.AfterThrowingAdviceInterceptor" /> <!-- employee proxy for after advice --> <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>afterThrowingAdviceInterceptor</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)
Test the Application that’s exactly …. Run it!
Let’s test using ApplicationContext
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 7:03:30 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@766e119d: startup date [Mon Aug 04 19:03:30 IST 2014]; root of context hierarchy Aug 04, 2014 7:03: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 updateEmployee : updates employee details based on the employee ID deleteEmployee : deletes employee based on the employee ID ******** AfterThrowingAdvice : getAllEmployee ******** 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.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:125) 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)
This way advice is applied to all business methods of the EmployeeService, but what if we want to restrict the advice should be advised to some of the particular business methods with after throwing 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 the 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" /> <!-- after Finally advice class --> <bean id="afterThrowingAdviceInterceptor" class="com.spring.series.aop.advice.AfterThrowingAdviceInterceptor" /> <!-- pointcut expression for after Finally advice --> <aop:config> <aop:aspect id="aspect" ref="afterThrowingAdviceInterceptor"> <aop:pointcut id="pointcutExpression" expression="execution(* com.spring.series.aop.service.*.get*(..))" /> <aop:after-throwing pointcut-ref="pointcutExpression" method="afterThrowingAdviceUsingPointcutExpression" /> </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 7:05:05 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@766e119d: startup date [Mon Aug 04 19:05:05 IST 2014]; root of context hierarchy Aug 04, 2014 7:05:05 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 updateEmployee : updates employee details based on the employee ID deleteEmployee : deletes employee based on the employee ID ******** After Throwing 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.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58) 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)
That’s not all; let’s move onto the next approach
Approach 3: Using AspectJ Annotations i.e.; (@Aspect / @AfterThrowing)
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 with annotated @Aspect and advice method with @AfterThrowing annotation for throws advice
AfterThrowingAdviceAnnotation.java
package com.spring.series.aop.advice; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; @Aspect public class AfterThrowingAdviceAnnotation { // execution(returnType qualified.package.name.classname.methodname(arguments)) @AfterThrowing("execution(* com.spring.series.aop.service.*.get*(..))") public void afterReturningUsingAnnotation() { System.out.println("******** After Throwing 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
- afterThrowingAdviceAspect bean for throws advice which is annotated with @Aspect/@AfterThrowing 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="afterThrowingAdviceAspect" class="com.spring.series.aop.advice.AfterThrowingAdviceAnnotation" /> </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 7:06:31 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6c1961f4: startup date [Mon Aug 04 19:06:31 IST 2014]; root of context hierarchy Aug 04, 2014 7:06:31 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 updateEmployee : updates employee details based on the employee ID deleteEmployee : deletes employee based on the employee ID ******** After Throwing 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.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58) 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 advice method annotated with @AfterThrowing 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 @AfterThrowing 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-Throwing-Advice (4kB) Spring-AOP-After-Throwing-Advice-using-Pointcut (4kB) Spring-AOP-After-Throwing-Advice-using-@AfterThrowing-Annotation(4kB)
Read Also:
- Spring AOP: Aspect Oriented Programming
- Spring AOP: Before Advice
- Spring AOP: After Returning Advice
- Spring AOP: After (Finally) Advice
- Spring AOP: Around Advice
Happy Coding !!
Happy Learning !!