Spring AOP Example
With this tutorial we shall show you how to use Aspect Oriented Programming in Spring Framework. AOP is used in the Spring Framework to provide declarative enterprise services, especially as a replacement for EJB declarative 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 Juno (4.2) version, along with Maven Integration plugin version 3.1.0. You can download Eclipse from here and Maven Plugin for Eclipse from here. The installation of Maven plugin for Eclipse is out of the scope of this tutorial and will not be discussed. We are also using Spring version 3.2.3 and the JDK 7_u_21.
Let’s begin.
1. Create a new Maven project
Go to File -> Project ->Maven -> Maven Project.
In the “Select project name and location” page of the wizard, make sure that “Create a simple project (skip archetype selection)” option is checked, hit “Next” to continue with default values.
In the “Enter an artifact id” page of the wizard, you can define the name and main package of your project. We will set the “Group Id” variable to "com.javacodegeeks.snippets.enterprise"
and the “Artifact Id” variable to "springexample"
. The aforementioned selections compose the main project package as "com.javacodegeeks.snippets.enterprise.springexample"
and the project name as "springexample"
. Hit “Finish” to exit the wizard and to create your project.
The Maven project structure is shown below:
- It consists of the following folders:
- /src/main/java folder, that contains source files for the dynamic content of the application,
- /src/test/java folder contains all source files for unit tests,
- /src/main/resources folder contains configurations files,
- /target folder contains the compiled and packaged deliverables,
- the pom.xml is the project object model (POM) file. The single file that contains all project related configuration.
2. Add Spring 3.2.3 dependency
- Locate the “Properties” section at the “Overview” page of the POM editor and perform the following changes:
Create a new property with name org.springframework.version and value 3.2.3.RELEASE. - Navigate to the “Dependencies” page of the POM editor and create the following dependencies (you should fill the “GroupId”, “Artifact Id” and “Version” fields of the “Dependency Details” section at that page):
Group Id : org.springframework Artifact Id : spring-web Version : ${org.springframework.version}
Alternatively, you can add the Spring dependencies in Maven’s pom.xml
file, by directly editing it at the “Pom.xml” page of the POM editor, as shown below:
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks.snippets.enterprise</groupId> <artifactId>springexample</artifactId> <version>0.0.1-SNAPSHOT</version> <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> </dependencies> <properties> <spring.version>3.2.3.RELEASE</spring.version> </properties> </project>
As you can see Maven manages library dependencies declaratively. A local repository is created (by default under {user_home}/.m2 folder) and all required libraries are downloaded and placed there from public repositories. Furthermore intra – library dependencies are automatically resolved and manipulated.
3. 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 create an example for each one of them.
SimpleService.java
class is the class whose methods will be intercepted by the advices we will create.
SimpleService.java
package com.javacodegeeks.snippets.enterprise; public class SimpleService { private String name; private int id; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public void printNameId() { System.out.println("SimpleService : Method printNameId() : My name is " + name + " and my id is " + id); } public void checkName() { if (name.length() < 20) { throw new IllegalArgumentException(); } } public void sayHello(String message){ System.out.println("SimpleService : Method sayHello() : Hello! " + message); } }
3.1 Before Advice
Before Advice executes before a method execution, but does not have the ability to prevent execution flow proceeding to the method execution (unless it throws an exception). The class that implements it is the one below:
DoBeforeMethod.java
package com.javacodegeeks.snippets.enterprise.aop; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class DoBeforeMethod implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("****SPRING AOP**** DoBeforeMethod : Executing before method!"); } }
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.
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doBeforeMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoBeforeMethod" /> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>doBeforeMethodBean</value> </list> </property> </bean> </beans>
We must load the simpleServiceProxy
bean in App.class
in order to run the application, as shown below:
App.java
package com.javacodegeeks.snippets.enterprise; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); SimpleService simpleService = (SimpleService) context.getBean("simpleServiceProxy"); simpleService.printNameId(); System.out.println("--------------"); try{ simpleService.checkName(); } catch(Exception e){ System.out.println("SimpleService: Method checkName() exception thrown.."); } System.out.println("--------------"); simpleService.sayHello("Javacodegeeks"); context.close(); } }
As a result, the before(Method method, Object[] args, Object target)
method of the DoBeforeMethod
Advice is invoked before the simpleService
‘s methods execution.
Output
****SPRING AOP**** DoBeforeMethod : Executing before method!
SimpleService : Method printNameId() : My name is Hello and my id is 12345
--------------
****SPRING AOP**** DoBeforeMethod : Executing before method!
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoBeforeMethod : Executing before method!
SimpleService : Method sayHello() : Hello! Javacodegeeks
3.2 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 that implements it is the one below:
DoAfterReturningMethod.java
package com.javacodegeeks.snippets.enterprise.aop; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class DoAfterReturningMethod implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("****SPRING AOP**** DoAfterReturningMethod : Executing after method return!"); } }
We add the new bean in applicationContext.xml
, following the same steps as above.
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doBeforeMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoBeforeMethod" /> <bean id="doAfterReturningMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAfterReturningMethod" /> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>doBeforeMethodBean</value> <value>doAfterReturningMethodBean</value> </list> </property> </bean> </beans>
Now, after running App.java
class again we can see that the afterReturning(Object returnValue, Method method, Object[] args, Object target)
method of DoAfterReturningMethod
advice is executed after the simpleService
‘s methods’ execution. Note that since checkName()
method throws an exception and does not return normally, it is not being intercepted by DoAfterReturningMethod
.
Output
****SPRING AOP**** DoBeforeMethod : Executing before method!
SimpleService : Method printNameId() : My name is Hello and my id is 12345
****SPRING AOP**** DoAfterReturningMethod : Executing after method return!
--------------
****SPRING AOP**** DoBeforeMethod : Executing before method!
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoBeforeMethod : Executing before method!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAfterReturningMethod : Executing after method return!
3.3 After Throwing Advice
After throwing Advice is the Advice to be executed if a method exits by throwing an exception. The class that implements it in the example is the one shown below:
DoAfterThrowingExceptionMethod.java
package com.javacodegeeks.snippets.enterprise.aop; import org.springframework.aop.ThrowsAdvice; public class DoAfterThrowingExceptionMethod implements ThrowsAdvice { public void afterThrowing(IllegalArgumentException e) throws Throwable { System.out.println("****SPRING AOP**** DoAfterThrowingExceptionMethod : Executing when method throws exception!"); } }
We add the new bean in applicationContext.xml
.
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doBeforeMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoBeforeMethod" /> <bean id="doAfterReturningMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAfterReturningMethod" /> <bean id="doAfterThrowingExceptionMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAfterThrowingExceptionMethod" /> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>doBeforeMethodBean</value> <value>doAfterReturningMethodBean</value> <value>doAfterThrowingExceptionMethodBean</value> </list> </property> </bean> </beans>
Now, after running the example again we can see that only the checkName()
method is being intercepted by the DoAfterThrowingExceptionMethod
.
Output
****SPRING AOP**** DoBeforeMethod : Executing before method!
SimpleService : Method printNameId() : My name is Hello and my id is 12345
****SPRING AOP**** DoAfterReturningMethod : Executing after method return!
--------------
****SPRING AOP**** DoBeforeMethod : Executing before method!
****SPRING AOP**** DoAfterThrowingExceptionMethod : Executing when method throws exception!
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoBeforeMethod : Executing before method!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAfterReturningMethod : Executing after method return!
3.4 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 that implements an around Advice is shown below:
DoAroundMethod.java
package com.javacodegeeks.snippets.enterprise.aop; import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class DoAroundMethod implements MethodInterceptor { public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("****SPRING AOP**** DoAroundMethod: Method name : " + methodInvocation.getMethod().getName()); System.out.println("****SPRING AOP**** DoAroundMethod: Method arguments : " + Arrays.toString(methodInvocation.getArguments())); // same with MethodBeforeAdvice System.out.println("****SPRING AOP**** DoAroundMethod: Before method executing!"); try { // proceed to original method call Object result = methodInvocation.proceed(); // same with AfterReturningAdvice System.out.println("****SPRING AOP**** DoAroundMethod: After method executing!"); return result; } catch (IllegalArgumentException e) { // same with ThrowsAdvice System.out.println("****SPRING AOP**** DoAroundMethod: When method throws Exception!"); throw e; } } }
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doBeforeMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoBeforeMethod" /> <bean id="doAfterReturningMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAfterReturningMethod" /> <bean id="doAfterThrowingExceptionMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAfterThrowingExceptionMethod" /> <bean id="doAroundMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod" /> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>doBeforeMethodBean</value> <value>doAfterReturningMethodBean</value> <value>doAfterThrowingExceptionMethodBean</value> <value>doAroundMethodBean</value> </list> </property> </bean> </beans>
When running the application with the DoAroundMethod
advice we can see that it intercepts all methods of simpleService
.
Output
****SPRING AOP**** DoBeforeMethod : Executing before method!
****SPRING AOP**** DoAroundMethod: Method name : printNameId
****SPRING AOP**** DoAroundMethod: Method arguments : []
****SPRING AOP**** DoAroundMethod: Before method executing!
SimpleService : Method printNameId() : My name is Hello and my id is 12345
****SPRING AOP**** DoAroundMethod: After method executing!
****SPRING AOP**** DoAfterReturningMethod : Executing after method return!
--------------
****SPRING AOP**** DoBeforeMethod : Executing before method!
****SPRING AOP**** DoAroundMethod: Method name : checkName
****SPRING AOP**** DoAroundMethod: Method arguments : []
****SPRING AOP**** DoAroundMethod: Before method executing!
****SPRING AOP**** DoAroundMethod: When method throws Exception!
****SPRING AOP**** DoAfterThrowingExceptionMethod : Executing when method throws exception!
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoBeforeMethod : Executing before method!
****SPRING AOP**** DoAroundMethod: Method name : sayHello
****SPRING AOP**** DoAroundMethod: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod: Before method executing!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAroundMethod: After method executing!
****SPRING AOP**** DoAfterReturningMethod : Executing after method return!
4. 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 ones that match a method by its name and the ones that match a method using a regular expression pattern. Let’s see how both types of Pointcuts can be used.
4.1 name match pointcut
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 applicationContext.xml
. In its property mappedName
, the value to be set is the name of the method that will be intercepted. Here, we will intercept sayHello(String message)
method.
We must also define the advisor as a bean of DefaultPointcutAdvisor
type, here simpleAdvisor
bean. Its properties are pointcut
and advice
and their values are references to the beans of the advice and the pointcut that will be used.
Finally, in simpleServiceProxy
bean we must replace the doAroundMethodBean
value of interceptorNames
property with the simpleAdvisor
.
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doAroundMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod" /> <bean id="simplePointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="sayHello" /> </bean> <bean id="simpleAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> <property name="pointcut" ref="simplePointcut" /> <property name="advice" ref="doAroundMethodBean" /> </bean> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>simpleAdvisor</value> </list> </property> </bean> </beans>
We run the App.class
again. Now, only the sayHello(String message)
method is being intercepted.
Output
SimpleService : Method printNameId() : My name is Hello and my id is 12345
--------------
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoAroundMethod: Method name : sayHello
****SPRING AOP**** DoAroundMethod: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod: Before method executing!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAroundMethod: After method executing!
Alternatively, you can use the NameMatchMethodPointcutAdvisor
, to combine both pointcut and advisor bean definitions in one bean, as shown below:
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doAroundMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod" /> <bean id="simpleAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName" value="sayHello" /> <property name="advice" ref="doAroundMethodBean" /> </bean> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>simpleAdvisor</value> </list> </property> </bean> </beans>
4.2 regex match pointcut
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 *Hello*
pattern, so again the sayHello(String message)
method will be intercepted. The advice
property holds a reference to the bean of the advice.
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doAroundMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod" /> <bean id="regexAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="patterns"> <list> <value>.*Hello.*</value> </list> </property> <property name="advice" ref="doAroundMethodBean" /> </bean> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>regexAdvisor</value> </list> </property> </bean> </beans>
After running App.java
class again we can see that only the method whose name matches the pattern *Hello*
is being intercepted by the advice. The result is shown below:
Output
SimpleService : Method printNameId() : My name is Hello and my id is 12345
--------------
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoAroundMethod: Method name : sayHello
****SPRING AOP**** DoAroundMethod: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod: Before method executing!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAroundMethod: After method executing!
5. 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 create a new Around method advice, DoAroundMethod2.java
class and add it to applicationContext.xml
as shown below:
DoAroundMethod2.java
package com.javacodegeeks.snippets.enterprise.aop; import java.util.Arrays; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class DoAroundMethod2 implements MethodInterceptor { public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("****SPRING AOP**** DoAroundMethod 2: Method name : " + methodInvocation.getMethod().getName()); System.out.println("****SPRING AOP**** DoAroundMethod 2: Method arguments : " + Arrays.toString(methodInvocation.getArguments())); // same with MethodBeforeAdvice System.out.println("****SPRING AOP**** DoAroundMethod 2: Before method executing!"); try { // proceed to original method call Object result = methodInvocation.proceed(); // same with AfterReturningAdvice System.out.println("****SPRING AOP**** DoAroundMethod 2: After method executing!"); return result; } catch (IllegalArgumentException e) { // same with ThrowsAdvice System.out.println("****SPRING AOP**** DoAroundMethod 2: When method throws Exception!"); throw e; } } }
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doAroundMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod" /> <bean id="doAroundMethod2Bean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod2" /> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>doAroundMethodBean</value> <value>doAroundMethod2Bean</value> </list> </property> </bean> </beans>
When running the application we can see that the around method advice whose bean is defined first in simpleServiceProxy
is the one that intercepts the methods first.
Output
****SPRING AOP**** DoAroundMethod: Method name : printNameId
****SPRING AOP**** DoAroundMethod: Method arguments : []
****SPRING AOP**** DoAroundMethod: Before method executing!
****SPRING AOP**** DoAroundMethod 2: Method name : printNameId
****SPRING AOP**** DoAroundMethod 2: Method arguments : []
****SPRING AOP**** DoAroundMethod 2: Before method executing!
SimpleService : Method printNameId() : My name is Hello and my id is 12345
****SPRING AOP**** DoAroundMethod 2: After method executing!
****SPRING AOP**** DoAroundMethod: After method executing!
--------------
****SPRING AOP**** DoAroundMethod: Method name : checkName
****SPRING AOP**** DoAroundMethod: Method arguments : []
****SPRING AOP**** DoAroundMethod: Before method executing!
****SPRING AOP**** DoAroundMethod 2: Method name : checkName
****SPRING AOP**** DoAroundMethod 2: Method arguments : []
****SPRING AOP**** DoAroundMethod 2: Before method executing!
****SPRING AOP**** DoAroundMethod 2: When method throws Exception!
****SPRING AOP**** DoAroundMethod: When method throws Exception!
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoAroundMethod: Method name : sayHello
****SPRING AOP**** DoAroundMethod: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod: Before method executing!
****SPRING AOP**** DoAroundMethod 2: Method name : sayHello
****SPRING AOP**** DoAroundMethod 2: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod 2: Before method executing!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAroundMethod 2: After method executing!
****SPRING AOP**** DoAroundMethod: After method executing!
Now, let’s change the sequence of the interceptors in simpleServiceProxy
bean and see what happens:
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doAroundMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod" /> <bean id="doAroundMethod2Bean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod2" /> <bean id="simpleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="simpleServiceBean" /> <property name="interceptorNames"> <list> <value>doAroundMethod2Bean</value> <value>doAroundMethodBean</value> </list> </property> </bean> </beans>
Again, the first defined interceptor is the one that intercepts the methods first:
Output
****SPRING AOP**** DoAroundMethod 2: Method name : printNameId
****SPRING AOP**** DoAroundMethod 2: Method arguments : []
****SPRING AOP**** DoAroundMethod 2: Before method executing!
****SPRING AOP**** DoAroundMethod: Method name : printNameId
****SPRING AOP**** DoAroundMethod: Method arguments : []
****SPRING AOP**** DoAroundMethod: Before method executing!
SimpleService : Method printNameId() : My name is Hello and my id is 12345
****SPRING AOP**** DoAroundMethod: After method executing!
****SPRING AOP**** DoAroundMethod 2: After method executing!
--------------
****SPRING AOP**** DoAroundMethod 2: Method name : checkName
****SPRING AOP**** DoAroundMethod 2: Method arguments : []
****SPRING AOP**** DoAroundMethod 2: Before method executing!
****SPRING AOP**** DoAroundMethod: Method name : checkName
****SPRING AOP**** DoAroundMethod: Method arguments : []
****SPRING AOP**** DoAroundMethod: Before method executing!
****SPRING AOP**** DoAroundMethod: When method throws Exception!
****SPRING AOP**** DoAroundMethod 2: When method throws Exception!
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoAroundMethod 2: Method name : sayHello
****SPRING AOP**** DoAroundMethod 2: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod 2: Before method executing!
****SPRING AOP**** DoAroundMethod: Method name : sayHello
****SPRING AOP**** DoAroundMethod: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod: Before method executing!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAroundMethod: After method executing!
****SPRING AOP**** DoAroundMethod 2: After method executing!
6. 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.
6.1 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 applicationContext.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.
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doAroundMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod" /> <bean id="simplePointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="sayHello" /> </bean> <bean id="simpleAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName" value="sayHello" /> <property name="advice" ref="doAroundMethodBean" /> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*ServiceBean</value> </list> </property> <property name="interceptorNames"> <list> <value>simpleAdvisor</value> </list> </property> </bean> </beans>
Now we can load the simpleServiceBean
in App.java
class, without having to know if this bean has a proxy object or not. The BeanNameAutoProxyCreator
will load the proxy.
App.java
package com.javacodegeeks.snippets.enterprise; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); SimpleService simpleService = (SimpleService) context.getBean("simpleServiceBean"); simpleService.printNameId(); System.out.println("--------------"); try{ simpleService.checkName(); } catch(Exception e){ System.out.println("SimpleService: Method checkName() exception thrown.."); } System.out.println("--------------"); simpleService.sayHello("Javacodegeeks"); context.close(); } }
The output is shown below:
Output
SimpleService : Method printNameId() : My name is Hello and my id is 12345
--------------
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoAroundMethod: Method name : sayHello
****SPRING AOP**** DoAroundMethod: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod: Before method executing!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAroundMethod: After method executing!
6.2 DefaultAdvisorAutoProxyCreator
The second auto proxy creator Spring provides is the DefaultAdvisorAutoProxyCreator
that automatically applies advisors in the current applicationContext.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 applicationContext.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.
applicationContext.xml
<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="simpleServiceBean" class="com.javacodegeeks.snippets.enterprise.SimpleService"> <property name="name" value="Hello" /> <property name="id" value="12345" /> </bean> <bean id="doAroundMethodBean" class="com.javacodegeeks.snippets.enterprise.aop.DoAroundMethod" /> <bean id="simplePointcut" class="org.springframework.aop.support.NameMatchMethodPointcut"> <property name="mappedName" value="sayHello" /> </bean> <bean id="simpleAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName" value="sayHello" /> <property name="advice" ref="doAroundMethodBean" /> </bean> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" /> </beans>
After running App.java
class again the result is shown below:
Output
SimpleService : Method printNameId() : My name is Hello and my id is 12345
--------------
SimpleService: Method checkName() exception thrown..
--------------
****SPRING AOP**** DoAroundMethod: Method name : sayHello
****SPRING AOP**** DoAroundMethod: Method arguments : [Javacodegeeks]
****SPRING AOP**** DoAroundMethod: Before method executing!
SimpleService : Method sayHello() : Hello! Javacodegeeks
****SPRING AOP**** DoAroundMethod: After method executing!
This was an example of Aspect Oriented Programming in Spring.
Download the Eclipse project of this tutorial : SpringAOPExample.zip