Advices, pointcuts. Obscure words making me wonder what Aspect-Oriented Programming (AOP) is all about. In fact, behind these words lie very simple principles. Once the AOP vocabulary has been tackled, using AOP is a matter of practice. Spring offers different techniques to use AOP, but I think that the variety of choice makes AOP even more confusing. Let's clear this up, and see how AOP can be used in Spring.
Two AOP implementations can be chosen, as well as two coding styles for each. Confusing, isn't it ?
- Spring AOP. It is not aimed to provide a full AOP implementation but it will be more than enough in most cases.
- using the @AspectJ annotation style
- using the XML configuration style
- AspectJ. If Spring AOP is not enough, AspectJ, a Java implementation of AOP, can be easily plugged in.
- using the AspectJ language syntax
- using the @AspectJ annotation style
- Part 1: Introduction to Spring AOP and proxies
- Part 2: Spring AOP using @AspectJ Annotation
- Part 3: Spring AOP using the XML schema
- Part 4: Spring AOP and transactions
AOP, what's the big deal ?
AOP is here to help programmers to separate of crosscutting concerns. What's that ? Imagine ordering an item at an online shop. When the item is ordered, several database tables may be updated. If a problem occurs during the ordering process, everything sent to the database should be cancelled (rollback). If the ordering process successfully ends, the ordering should be committed to the databases.[Before]
public class OrderServiceImpl implements OrderService { public void orderItem(Item item, int quantity) { // Insert item into ORDER table ... // Update ITEM table, decrement the number of remaining items ... } ... } public class OrderItem { public void orderItem(Item item, int quantity) { OrderService orderService = ServiceManager.getOrderService(); orderService.orderItem(item, quantity); } }
[After]
public class OrderServiceImpl implements OrderService { public void orderItem(Item item, int quantity) { // Start transaction ... // Insert item into ORDER table ... // Update ITEM table, decrement the number of remaining items ... // Commit changes ... } } public class OrderItem { public void orderItem(Item item, int quantity) { try { OrderService orderService = ServiceManager.getOrderService(); orderService.orderItem(item, quantity); } catch( DbException e ) { // log exception ... // Rollback transaction ... } } }
Proxy
A proxy is a well-used design pattern. To put it simply, a proxy is an object that looks like another object, but adds special functionality behind the scene. In Spring, any interface can be proxied. Let's see a simple example to illustrate what a proxy really is. The Saloon interface:package com.javaranch.journal.spring.aop; public interface Saloon { void openSaloon(); }
package com.javaranch.journal.spring.aop; public class MooseSaloon implements Saloon { /** Welcome message */ private String greeting; public void setGreeting(String greeting) { this.greeting = greeting; } public void openSaloon() { System.out.println("Saloon open [" + getClass() + "/" + toString() + "]"); System.out.println(greeting); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="mooseSaloon" class="com.javaranch.journal.spring.aop.MooseSaloon"> <property name="greeting"><value>Welcome to the Big Moose Saloon !!</value></property> </bean> <bean id="proxySaloon" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.javaranch.journal.spring.aop.Saloon</value></property> <property name="target"><ref local="mooseSaloon"/></property> </bean> </beans>
package com.javaranch.journal.spring.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml"); // The mooseSaloon pojo Saloon mooseSaloon = (Saloon)ctx.getBean("mooseSaloon"); mooseSaloon.openSaloon(); System.out.println("mooseSaloon [" + mooseSaloon.getClass() + "]"); System.out.println("--------"); // The mooseSaloon proxy Saloon proxySaloon = (Saloon)ctx.getBean("proxySaloon"); proxySaloon.openSaloon(); System.out.println("proxySaloon [" + proxySaloon.getClass() + "]"); } }
Saloon open [class com.javaranch.journal.spring.aop.MooseSaloon/ com.javaranch.journal.spring.aop.MooseSaloon@bfea1d] Welcome to the Big Moose Saloon !! mooseSaloon [class com.javaranch.journal.spring.aop.MooseSaloon] -------- Saloon open [class com.javaranch.journal.spring.aop.MooseSaloon/ com.javaranch.journal.spring.aop.MooseSaloon@bfea1d] Welcome to the Big Moose Saloon !! proxySaloon [class $Proxy0]
AOP jargon
The AOP terminology may be boring to get on with, but it is necessary to understand these words before going further.- Join point : a particular execution point. For example a method execution, or the handling of an exception.
- Advice : simply said, it is the code to execute. For example, starting a transaction.
- Pointcut : represents which join point an advice should be applied to
- Aspect : pointcuts and advices form an aspect
- Target object : the object being advised by some aspects
- Weaving : "applying an aspect"
- Aspect : start a transaction in methods which need to run in a transactional context
- Join point : method is called
- Pointcut : every method starting with "insert", "delete", "update"
- Advice : start a transaction
There are different types of advices
- Before advice : executes before a join point
- After returning advice : executes after a join point completes without throwing exceptions
- After throwing advice : executes after a join point has thrown an exception
- After (finally) advice : executes after a join point has finished, not matter if it has thrown an exception or not
- Around advice : executes before and after a join point is executed
A simple advice
In the MooseSaloon example, let's apply a simple aspect :- Aspect : log every Saloon methods called
- Pointcut : every Saloon methods of the MooseSaloon
- Advice : log before the method is called
package com.javaranch.journal.spring.aop; import java.lang.reflect.Method; import java.util.Date; import org.springframework.aop.MethodBeforeAdvice; public class SaloonWatcher implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("[" + new Date() + "]" + method.getName() + " called on " + target); } }
Let's now modify the proxy to use this advice:
<bean id="proxySaloon" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.javaranch.journal.spring.aop.Saloon</value></property> <property name="target"><ref local="mooseSaloon"/></property> <property name="interceptorNames"> <list> <value>saloonWatcher</value> </list> </property> </bean>
That's all. The Main class and the MooseSaloon class do not change. After execution, the printed information will look like this:
Saloon open [class com.javaranch.journal.spring.aop.MooseSaloon/ com.javaranch.journal.spring.aop.MooseSaloon@bfea1d] Welcome to the Big Moose Saloon !! mooseSaloon [class com.javaranch.journal.spring.aop.MooseSaloon] -------- [Mon Mar 24 15:14:42 JST 2008]openSaloon called on com.javaranch.journal.spring.aop.MooseSaloon@2a4983 Saloon open [class com.javaranch.journal.spring.aop.MooseSaloon/ com.javaranch.journal.spring.aop.MooseSaloon@bfea1d] Welcome to the Big Moose Saloon !! proxySaloon [class $Proxy0]
A word on proxying beans
In the above example, there is a MooseSaloon and its proxy. In the main method, the proxy is explicitly instanciated via its name "proxySaloon". Wouldn't it be cool if we could use a proxy without even knowing it ? Actually, Spring uses a powerful feature called autoproxying, which will proxy selected beans behind the scene. There are different ways to achieve this. Here is one using the BeanNameAutoProxyCreator, which automatically create proxies depending on bean names. In part two of this tutorial, we will see how autoproxying is achieved using the @AspectJ annotations. But to illustrate the concept of autoproxying, let's modify the Main class, the bean definition file, and use a BeanNameAutoProxyCreator. Let's remove the proxy from the Main class.package com.javaranch.journal.spring.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("aop.xml"); // The mooseSaloon pojo Saloon mooseSaloon = (Saloon)ctx.getBean("mooseSaloon"); mooseSaloon.openSaloon(); System.out.println("mooseSaloon [" + mooseSaloon.getClass() + "]"); } }
And let's remove it from the definition file too. Instead, we will use the BeanNameAutoProxyCreator to create a proxy automatically on the MooseSaloon.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="mooseSaloon" class="com.javaranch.journal.spring.aop.MooseSaloon"> <property name="greeting"><value>Welcome to the Big Moose Saloon !!</value></property> </bean> <bean id="saloonWatcher" class="com.javaranch.journal.spring.aop.SaloonWatcher" /> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"><value>*Saloon</value></property> <property name="interceptorNames"> <list> <value>saloonWatcher</value> </list> </property> </bean> </beans>
A proxy will be created for every bean whose name ends in "Saloon", just like our "mooseSaloon". After executing this new version, something similar to the following should be printed:
[Mon Mar 24 16:00:56 JST 2008]openSaloon called on com.javaranch.journal.spring.aop.MooseSaloon@5a9de6 Saloon open [class com.javaranch.journal.spring.aop.MooseSaloon/ com.javaranch.journal.spring.aop.MooseSaloon@5a9de6] Welcome to the Big Moose Saloon !! mooseSaloon [class $Proxy0]