With this tutorial we will see how to use Aspect Oriented Programming in Spring Framework. AOP is used in the Spring Framework to provide declarative enterprise services. It is also used to allow users to implement custom aspects, complementing their use of OOP with AOP.
We will demonstrate how to create and apply some central AOP concepts. In short we will create all types of advices, we will use a pointcut and an advisor over an advice and we will check on some special features of Spring in interceptors and proxy beans.
Our preferred development environment is Eclipse. We are using Eclipse Kepler version. We are also using Spring version 3.2.8 and the JDK 1.6.
Make sure you have the list of required jar files in your classpath.
Create an Advice
In AOP the Advice is an action taken before or after a method execution. There are different types of advice, such as around, before and after advice. Below we introduce all types of advice and show an example for each one of them.
AOPService.java class is the class whose methods will be intercepted by the advices
package in.sblog.spring.aop.service; public class AOPService { private int aopId; private String aopName; public int getAopId() { return aopId; } public void setAopId(int aopId) { this.aopId = aopId; } public String getAopName() { return aopName; } public void setAopName(String aopName) { this.aopName = aopName; } public void printAOPName() { System.out.println("inside AOPService::printAOPName."); System.out.println("AOP Id:" + aopId + ", AOP Name: " + aopName); System.out.println(); } public void isValidNameLength() { if (aopName != null || !aopName.trim().isEmpty()) { if (aopName.trim().length() < 6 || aopName.trim().length() > 25) { throw new IllegalArgumentException( "Name must not less than 3 chars and greater than 25 chars long!"); } else { System.out.println("Name is valid!"); } } else { throw new IllegalArgumentException("Name canot be null!"); } System.out.println(); } public void printAnyMessage(String msg) { System.out.println("Message: " + msg); } }
Before Advice
Before Advice executes before a method execution, but it does not have the ability to prevent execution flow proceeding to the method execution unless the method throws an exception. Our class has to implement the interface MethodBeforeAdvice.
package in.sblog.spring.aop.advice; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class AOPBeforeMethod implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("AOPBeforeMethod.before() overrides MethodBeforeAdvice.before()"); System.out.println("Method which gets executed: " + method.getName()); if (args.length > 0) { System.out.println("Arguments passed to the method:"); for (int i = 0; i < args.length; i++) { System.out.println("args[" + i + "]: " + args[i]); } } System.out.println("Target where the advice is applied on: " + target); System.out.println(); } }
The advice bean must be defined in Spring configuration file. In addition, a proxy object must be created, of ProxyFactoryBean type. The proxy bean has a target property. Its value is a reference to the bean whose methods will be intercepted. It also has an interceptorNames property. The property value is a list of bean names that represent the advices that will be applied on this proxy/target object.
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopBeforeMethodBean" class="in.sblog.spring.aop.advice.AOPBeforeMethod"></bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopBeforeMethodBean</value> </list> </property> </bean> </beans>
We must load the aopServiceProxy bean in TestAOP.java in order to run the application, as shown below:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAOP { public static void main(String[] args) { @SuppressWarnings("resource") ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "aopContext.xml"); AOPService aopService = (AOPService) applicationContext .getBean("aopServiceProxy"); System.out.println("--------------------------------------------"); aopService.printAOPName(); System.out.println("--------------------------------------------"); System.out.println(); System.out.println("--------------------------------------------"); aopService.isValidNameLength(); System.out.println("--------------------------------------------"); System.out.println(); System.out.println("--------------------------------------------"); aopService.printAnyMessage("from www.sblog.in tutorial"); System.out.println("--------------------------------------------"); } }
As a result, the before(Method method, Object[] args, Object target) method of the AOPBeforeMethod Advice is invoked before the simpleService‘s methods execution.
Output:
-------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: printAOPName Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@23e5d1 inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in -------------------------------------------- -------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: isValidNameLength Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@23e5d1 Name is valid! -------------------------------------------- -------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@23e5d1 Message: from www.sblog.in tutorial --------------------------------------------
After Returning Advice
After returning advice is the Advice to be executed after a method execution completes normally: for example, if a method returns without throwing an exception. The class has to implement the interface AfterReturningAdvice.
import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class AOPAfterReturningMethod implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out .println("AOPAfterReturningMethod.afterReturning() overrides AfterReturningAdvice.afterReturning()"); System.out.println("Method which returns: " + returnValue); System.out.println("Method which gets executed: " + method.getName()); if (args.length > 0) { System.out.println("Arguments passed to the method:"); for (int i = 0; i < args.length; i++) { System.out.println("args[" + i + "]: " + args[i]); } } System.out.println("Target where the advice is applied on: " + target); System.out.println(); } }
We add the new bean in aopContext.xml, following the same steps as above.
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopBeforeMethodBean" class="in.sblog.spring.aop.advice.AOPBeforeMethod"></bean><bean id="aopAfterReturningMethodBean" class="in.sblog.spring.aop.advice.AOPAfterReturningMethod"></bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopBeforeMethodBean</value> <value>aopAfterReturningMethodBean</value> </list> </property> </bean> </beans>
Now, after running TestAOP.java class again we can see that the afterReturning(Object returnValue, Method method, Object[] args, Object target) method of AOPAfterReturningMethod advice is executed after the AOPService‘s methods’ execution.
Output:
-------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: printAOPName Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@b8c8e6 inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in AOPAfterReturningMethod.afterReturning() overrides AfterReturningAdvice.afterReturning() Method which returns: null Method which gets executed: printAOPName Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@b8c8e6 -------------------------------------------- -------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: isValidNameLength Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@b8c8e6 Name is valid! AOPAfterReturningMethod.afterReturning() overrides AfterReturningAdvice.afterReturning() Method which returns: null Method which gets executed: isValidNameLength Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@b8c8e6 -------------------------------------------- -------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@b8c8e6 Message: from www.sblog.in tutorial AOPAfterReturningMethod.afterReturning() overrides AfterReturningAdvice.afterReturning() Method which returns: null Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@b8c8e6 --------------------------------------------
After Throwing Advice
After throwing Advice is the Advice to be executed if a method exits by throwing an exception. The class has to implement the ThrowsAdvice interface.
import org.springframework.aop.ThrowsAdvice; public class AOPAfterThrowingExceptionMethod implements ThrowsAdvice { public void afterThrowing(IllegalArgumentException e) { System.out.println("AOPAfterThrowingExceptionMethod implements ThrowsAdvice"); System.out .println("Executes when a particular method throws an Exception"); System.out.println("The exception trace is below:"); System.out.println(e); } }
We add the new bean in aopContext.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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"></pre> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog"></property> </bean><bean id="aopBeforeMethodBean" class="in.sblog.spring.aop.advice.AOPBeforeMethod"></bean><bean id="aopAfterReturningMethodBean" class="in.sblog.spring.aop.advice.AOPAfterReturningMethod"></bean><bean id="aopAOPAfterThrowingExceptionMethodBean" class="in.sblog.spring.aop.advice.AOPAfterThrowingExceptionMethod"></bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopBeforeMethodBean</value> <value>aopAfterReturningMethodBean</value> <value>aopAOPAfterThrowingExceptionMethodBean</value> </list> </property> </bean> </beans>
Now, after running the example again we can see that only the isValidNameLength() method is being intercepted by the AOPAfterThrowingExceptionMethod.
Output:
-------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: printAOPName Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@176e552 inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog AOPAfterReturningMethod.afterReturning() overrides AfterReturningAdvice.afterReturning() Method which returns: null Method which gets executed: printAOPName Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@176e552 -------------------------------------------- -------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: isValidNameLength Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@176e552 AOPAfterThrowingExceptionMethod implements ThrowsAdvice Executes when a particular method throws an Exception The exception trace is below: java.lang.IllegalArgumentException: Name must not less than 3 chars and greater than 25 chars long! Exception in thread "main" java.lang.IllegalArgumentException: Name must not less than 3 chars and greater than 25 chars long! at in.sblog.spring.aop.service.AOPService.isValidNameLength(AOPService.java:33) at in.sblog.spring.aop.service.AOPService$$FastClassBySpringCGLIB$$baddd1d7.invoke() at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:700) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:124) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:51) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:51) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:633) at in.sblog.spring.aop.service.AOPService$$EnhancerBySpringCGLIB$$6207e9f3.isValidNameLength() at in.sblog.spring.aop.test.TestAOP.main(TestAOP.java:22)
Around Advice
Around advice is the Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception. The class has to implement the interface MethodInterceptor.
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class AOPARoundMethod implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out .println("AOPARoundMethod.invoke() overrides MethodInterceptor.invoke()"); System.out.println("Method which gets executed: " + methodInvocation.getMethod().getName()); if (methodInvocation.getArguments().length > 0) { System.out.println("Arguments passed to the method:"); for (int i = 0; i < methodInvocation.getArguments().length; i++) { System.out.println("args[" + i + "]: " + methodInvocation.getArguments()[i]); } } System.out.println("Before Method executing"); try { Object result = methodInvocation.proceed(); System.out.println("After Method executing"); return result; } catch (Throwable t) { System.out.println("When Exceptions are thrown"); throw t; } } }
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopBeforeMethodBean" class="in.sblog.spring.aop.advice.AOPBeforeMethod"></bean><bean id="aopAfterReturningMethodBean" class="in.sblog.spring.aop.advice.AOPAfterReturningMethod"></bean><bean id="aopAOPAfterThrowingExceptionMethodBean" class="in.sblog.spring.aop.advice.AOPAfterThrowingExceptionMethod"></bean><bean id="aopAOPAroundMethodBean" class="in.sblog.spring.aop.advice.AOPARoundMethod"></bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopBeforeMethodBean</value> <value>aopAfterReturningMethodBean</value> <value>aopAOPAfterThrowingExceptionMethodBean</value> <value>aopAOPAroundMethodBean</value> </list> </property> </bean> </beans>
When running the application with the AOPARoundMethod advice we can see that it intercepts all methods of simpleService.
Output:
-------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: printAOPName Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@c980c9 AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAOPName Before Method executing inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in After Method executing AOPAfterReturningMethod.afterReturning() overrides AfterReturningAdvice.afterReturning() Method which returns: null Method which gets executed: printAOPName Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@c980c9 -------------------------------------------- -------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: isValidNameLength Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@c980c9 AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: isValidNameLength Before Method executing Name is valid! After Method executing AOPAfterReturningMethod.afterReturning() overrides AfterReturningAdvice.afterReturning() Method which returns: null Method which gets executed: isValidNameLength Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@c980c9 -------------------------------------------- -------------------------------------------- AOPBeforeMethod.before() overrides MethodBeforeAdvice.before() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@c980c9 AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing Message: from www.sblog.in tutorial After Method executing AOPAfterReturningMethod.afterReturning() overrides AfterReturningAdvice.afterReturning() Method which returns: null Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Target where the advice is applied on: in.sblog.spring.aop.service.AOPService@c980c9 --------------------------------------------
Create a Pointcut and an Advisor
The Pointcut indicates which method should be intercepted whereas the Advisor groups the Advice and the Pointcut into a single unit, and passes it to a proxy factory object.
There are two types of Pointcuts, the one that matches a method by its name and the one that matches a method using a regular expression pattern. Let’s see how both types of Pointcuts can be used.
Pointcut matching a name
In order to create a new Pointcut that will match a method by its name, we have to define it as a bean of NameMatchMethodPointcut type in aopContext.xml. In its property mappedName, the value to be set is the name of the method that will be intercepted. Here, we will intercept printAnyMessage(String msg) method.
We must also define the advisor as a bean of DefaultPointcutAdvisor type, here aopServiceBean bean. Its properties are pointcut; advice and their values are references to the beans of the advice and the pointcut that will be used.
Finally, in aopServiceProxy bean we must replace the AOPARoundMethod value of interceptorNames property with the aopServiceBean.
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopAOPAroundMethodBean" class="in.sblog.spring.aop.advice.AOPARoundMethod"></bean><bean id="aopPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="printAnyMessage"></property> </bean><bean id="aopAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="aopPointcut"></property> <property name="advice" ref="aopAOPAroundMethodBean"></property> </bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopAdvisor</value> </list> </property> </bean> </beans>
We run the TestAOP.java again. Now, only the printAnyMessage(String msg) method is being intercepted.
Output:
-------------------------------------------- inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in -------------------------------------------- -------------------------------------------- Name is valid! -------------------------------------------- -------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing Message: from www.sblog.in tutorial After Method executing --------------------------------------------
Alternatively, we can use the NameMatchMethodPointcutAdvisor, to combine both pointcut and advisor bean definitions in one bean, as shown below:
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopAOPAroundMethodBean" class="in.sblog.spring.aop.advice.AOPARoundMethod"></bean><bean id="aopAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName" value="printAnyMessage"></property> <property name="advice" ref="aopAOPAroundMethodBean"></property> </bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopAdvisor</value> </list> </property> </bean> </beans>
Output:
-------------------------------------------- inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in -------------------------------------------- -------------------------------------------- Name is valid! -------------------------------------------- -------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing Message: from www.sblog.in tutorial After Method executing --------------------------------------------
Pointcut matching a regex
Now, in order to create a pointcut that matches the method to be intercepted with a regular exression, we must define a new bean of RegexpMethodPointcutAdvisor type. The specific type of advisor has two properties. The patterns property holds a list of the patterns that are used for selecting the methods by their names that will be intercepted and applied the advice code. Here, in regexAdvisor we have used the *sblog* pattern, so again the printAnyMessage(String msg) method will be intercepted. The advice property holds a reference to the bean of the advice.
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopAOPAroundMethodBean" class="in.sblog.spring.aop.advice.AOPARoundMethod"></bean><bean id="aopAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="patterns"> <list> <value>.*sblog.*</value> </list> </property> <property name="advice" ref="aopAOPAroundMethodBean"></property> </bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopAdvisor</value> </list> </property> </bean> </beans>
After running TestAOP.java class again we can see that only the method whose name matches the pattern *sblog* is being intercepted by the advice. The result is shown below:
Output:
-------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAOPName Before Method executing inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in After Method executing -------------------------------------------- -------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: isValidNameLength Before Method executing Name is valid! After Method executing -------------------------------------------- -------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing Message: from www.sblog.in tutorial After Method executing --------------------------------------------
AOP interceptors sequence
Now, let’s see how the sequence of the values in interceptorNames property of the proxy object can affect the sequence in which the advices intercept the methods. We need to create a new Around method advice, AOPARoundMethod.java class and add it to aopContext.xml as shown below:
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class AOPARoundMethod2 implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out .println("AOPARoundMethod2.invoke() overrides MethodInterceptor.invoke()"); System.out.println("Method which gets executed: " + methodInvocation.getMethod().getName()); if (methodInvocation.getArguments().length > 0) { System.out.println("Arguments passed to the method:"); for (int i = 0; i < methodInvocation.getArguments().length; i++) { System.out.println("args[" + i + "]: " + methodInvocation.getArguments()[i]); } } System.out.println("Before Method executing"); try { Object result = methodInvocation.proceed(); System.out.println("After Method executing"); return result; } catch (Throwable t) { System.out.println("When Exceptions are thrown"); throw t; } } }
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopAOPAroundMethodBean" class="in.sblog.spring.aop.advice.AOPARoundMethod"></bean><bean id="aopAOPAroundMethodBean2" class="in.sblog.spring.aop.advice.AOPARoundMethod2"></bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopAOPAroundMethodBean</value> <value>aopAOPAroundMethodBean2</value> </list> </property> </bean> </beans>
When running the application we can see that the around method advice whose bean is defined first in aopServiceProxy is the one that intercepts the methods first.
Output:
-------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAOPName Before Method executing AOPARoundMethod2.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAOPName Before Method executing inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in After Method executing After Method executing -------------------------------------------- -------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: isValidNameLength Before Method executing AOPARoundMethod2.invoke() overrides MethodInterceptor.invoke() Method which gets executed: isValidNameLength Before Method executing Name is valid! After Method executing After Method executing -------------------------------------------- -------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing AOPARoundMethod2.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing Message: from www.sblog.in tutorial After Method executing After Method executing --------------------------------------------
Now, let’s change the sequence of the interceptors in aopServiceProxy bean and see what happens:
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopAOPAroundMethodBean" class="in.sblog.spring.aop.advice.AOPARoundMethod"></bean><bean id="aopAOPAroundMethodBean2" class="in.sblog.spring.aop.advice.AOPARoundMethod2"></bean><bean id="aopServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="aopServiceBean"></property> <property name="interceptorNames"> <list> <value>aopAOPAroundMethodBean2</value> <value>aopAOPAroundMethodBean</value> </list> </property> </bean> </beans>
Again, the first defined interceptor is the one that intercepts the methods first:
Output:
-------------------------------------------- AOPARoundMethod2.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAOPName Before Method executing AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAOPName Before Method executing inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in After Method executing After Method executing -------------------------------------------- -------------------------------------------- AOPARoundMethod2.invoke() overrides MethodInterceptor.invoke() Method which gets executed: isValidNameLength Before Method executing AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: isValidNameLength Before Method executing Name is valid! After Method executing After Method executing -------------------------------------------- -------------------------------------------- AOPARoundMethod2.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing Message: from www.sblog.in tutorial After Method executing After Method executing --------------------------------------------
AOP auto proxy creators
An interesting feature of Spring is that it provides two auto proxy creators, so that we can create proxies for our beans automatically.
BeanNameAutoProxyCreator
The first auto proxy creator Spring provides is the BeanNameAutoProxyCreator that automatically creates AOP proxies for beans with names matching literal values or wildcards. In order to use it we must define it in aopContext.xml. This creator exposes two properties for us to configure. The first property is beanNames and its value is a list of regular expressions matching the Spring bean names (ids) to be proxied. The second property is interceptorNames and its value is a list of the advisors (Spring bean ids) that will be used.
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopAOPAroundMethodBean" class="in.sblog.spring.aop.advice.AOPARoundMethod"></bean><bean id="aopPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="printAnyMessage"></property> </bean><bean id="aopAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName" value="printAnyMessage"></property> <property name="advice" ref="aopAOPAroundMethodBean"></property> </bean><bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*ServiceBean</value> </list> </property> <property name="interceptorNames"> <list> <value>aopAdvisor</value> </list> </property> </bean> </beans>
Now we can load the aopServiceBean in TestAOP.java class, without having to know if this bean has a proxy object or not. The BeanNameAutoProxyCreator will load the proxy.
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestAOP { public static void main(String[] args) { @SuppressWarnings("resource") ApplicationContext applicationContext = new ClassPathXmlApplicationContext( "aopContext.xml"); AOPService aopService = (AOPService) applicationContext .getBean("aopServiceBean"); System.out.println("--------------------------------------------"); aopService.printAOPName(); System.out.println("--------------------------------------------"); System.out.println(); System.out.println("--------------------------------------------"); aopService.isValidNameLength(); System.out.println("--------------------------------------------"); System.out.println(); System.out.println("--------------------------------------------"); aopService.printAnyMessage("from www.sblog.in tutorial"); System.out.println("--------------------------------------------"); } }
Output:
-------------------------------------------- inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in -------------------------------------------- -------------------------------------------- Name is valid! -------------------------------------------- -------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing Message: from www.sblog.in tutorial After Method executing --------------------------------------------
DefaultAdvisorAutoProxyCreator
The second auto proxy creator Spring provides is the DefaultAdvisorAutoProxyCreator that automatically applies advisors in the current aopContext.xml, without the need to include specific bean names in the auto-proxy advisor’s bean definition. In order to use it we must specify a DefaultAdvisorAutoProxyCreator bean definition in aopContext.xml. Then we must specify any number of advisors in the same or related configuration files. The DefaultAdvisorAutoProxyCreator will automatically evaluate the pointcut contained in each advisor, to see what (if any) advice it should apply to each object.
<?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:p="http://www.springframework.org/schema/p" 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:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xsi:schemalocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"><bean id="aopServiceBean" class="in.sblog.spring.aop.service.AOPService"> <property name="aopId" value="1000"></property> <property name="aopName" value="sblog.in"></property> </bean><bean id="aopPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="printAnyMessage"></property> </bean><bean id="aopAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName" value="printAnyMessage"></property> <property name="advice" ref="aopAOPAroundMethodBean"></property> </bean><bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean> </beans>
After running TestAOP.java class again the result is shown below:
Output:
-------------------------------------------- inside AOPService::printAOPName. AOP Id:1000, AOP Name: sblog.in -------------------------------------------- -------------------------------------------- Name is valid! -------------------------------------------- -------------------------------------------- AOPARoundMethod.invoke() overrides MethodInterceptor.invoke() Method which gets executed: printAnyMessage Arguments passed to the method: args[0]: from www.sblog.in tutorial Before Method executing Message: from www.sblog.in tutorial After Method executing --------------------------------------------
Thanks for your reading. Please do not forget to leave a comment.