The main aim of Spring AOP is to allow the realization of JEE functionalities in the simplest manner and without being intrusive. With this aim, it allows the use of a subset of AOP functionalities in a simple and intuitive way (introduced since version 1.x, and in version 2.x with new integrations with AspectJ).
In order to achieve this aim, since version 1.x, Spring has implemented the specifications of the AOP alliance. This is a joint effort between representatives of many open-source AOP projects, including Rod Johnson of Spring, to define a standard set of interfaces for AOP implementations.
In Spring AOP, an aspect is represented by an instance of a class that implements the Advisor interface. There are two subinterfaces of Advisor: IntroductionAdvisor
and PointcutAdvisor
. The PointcutAdvisor
interface is implemented by all Advisors that use pointcuts to control the applicability of advice to joinpoints.
In Spring, introductions are treated as special kinds of advice. Using the IntroductionAdvisor
interface, you can control those classes to which an introduction applies.
The core of Spring AOP is based around proxies. There are two ways of using proxies: programmatic modality and declarative modality.
The former consists of using a ProxyFactory
to create a proxy of the class on which you want to apply an aspect. After creating the proxy, you use the ProxyFactory
to weave all the aspects you want to use on the object.
The ProxyFactory
class controls the weaving and proxy creation process in Spring.
Using the ProxyFactory
class, you control which aspects you want to weave into the proxy. You can weave only an aspect, that is, advice combined with a pointcut.
However, in some cases you want an advice to apply to the invocation of all methods in a class, not just a selection. For this reason, the ProxyFactory
class provides the addAdvice()
method. Internally, addAdvice()
wraps the advice you pass it in an instance of DefaultPointcutAdvisor
, and configures it with a pointcut that includes all methods by default.
This is an example of class that implements the MethodBeforeAdvice
to perform a crosscutting functionality before the method of the target class.
Before advice is performed before the invocation of the method.
Let us see an example that shows the usage of before advice, with a class that implements the MethodBeforeAdvice
, and has a main
method for testing.
package org.springaop.chapter.one; import java.lang.reflect.Method; import org.springaop.target.Hello; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.aop.framework.ProxyFactory; public class BeforeAdvice implements MethodBeforeAdvice{ public static void main(String[] args) { //target class Hello target = new Hello(); // create the proxy ProxyFactory pf = new ProxyFactory(); // add advice pf.addAdvice(new BeforeAdvice()); // setTarget Spring AOPbefore advice methodpf.setTarget(target); Hello proxy = (Hello) pf.getProxy(); proxy.greeting(); } public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("Good morning"); } } public class Hello { public void greeting(){ System.out.println("reader"); } }
The result will be:
After returning advice is performed after the invocation of the method.
package org.springaop.chapter.one; import java.lang.reflect.Method; import org.springaop.target.Hello; import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.framework.ProxyFactory; public class AfterRetuningAdvice implements AfterReturningAdvice { public static void main(String[] args) { // target class Hello target = new Hello(); // create the proxy ProxyFactory pf = new ProxyFactory(); // add advice pf.addAdvice(new AfterRetuningAdvice()); // setTarget pf.setTarget(target); Hello proxy = (Hello) pf.getProxy(); proxy.greeting(); } public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(",this is a afterReturningAdvice message"); } } public class Hello { public void greeting(){ System.out.println("reader"); } }
The result will be:
This is the Hello
target class on which we want to apply an around advice. It is called before the method and controls its invocation.
public class Hello { public void greeting(){ System.out.println("reader"); } }
This is the advice that must be applied around the performed method; as we can see that the invocation of the method occurs with invocation.proceed
.
package org.springaop.chapter.one; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MethodDecorator implements MethodInterceptor{ public Object invoke(MethodInvocation invocation) throws Throwable { System.out.print("Hello "); Object retVal = invocation.proceed(); System.out.println("this is aop !"); return retVal; } }
This is the class where, through the ProxyFactory
, we give the advice to apply. But, in the case of the MethodDecorator
, it is an around advice.
package org.springaop.chapter.one; import org.springaop.target.Hello; import org.springframework.aop.framework.ProxyFactory; public class AroundAdvice { public static void main(String[] args) { //target class Hello target = new Hello(); // create the proxy ProxyFactory pf = new ProxyFactory(); // add advice pf.addAdvice(new MethodDecorator()); // setTarget pf.setTarget(target); Hello proxy = (Hello) pf.getProxy(); proxy.greeting(); } }
The result will be:
This advice is performed only if the method on which the advice is applied throws an exception.
This is a class that intentionally throws an exception in every method; the exceptions are of different types.
package org.springaop.target; public class ExceptionTarget { public void errorMethod() throws Exception { throw new Exception("Fake exception"); } public void otherErrorMethod() throws IllegalArgumentException { throw new NullPointerException("Other Fake exception"); } }
This is the code to try it:
package org.springaop.chapter.one; import java.lang.reflect.Method; import org.springaop.target.ExceptionTarget; import org.springframework.aop.ThrowsAdvice; import org.springframework.aop.framework.ProxyFactory; public class ThrowsAdviceClass implements ThrowsAdvice { public static void main(String[] args) { //target class ExceptionTarget errorBean = new ExceptionTarget(); // create the proxy ProxyFactory pf = new ProxyFactory(); // add advice pf.addAdvice(new ThrowsAdviceClass()); // setTarget pf.setTarget(errorBean); ExceptionTarget proxy = (ExceptionTarget) pf.getProxy(); try { proxy.errorMethod(); } catch (Exception ignored) { } try { proxy.otherErrorMethod(); } catch (Exception ignored) { } } public void afterThrowing(Exception ex) throws Throwable { System.out.println("+++"); System.out.println("Exception Capture:"+ex.getClass().getName()); System.out.println("+++\n"); } public void afterThrowing(Method method, Object[] args, Object target, NullPointerException ex) throws Throwable { System.out.println("+++"); System.out.println("NullPointerException Capture: "+ex.getClass().getName()); System.out.println("Method: " + method.getName()); System.out.println("+++\n"); } }
The result will be:
Here we will see how to use the examples described previously, configuring the classes as Spring beans declared in XML file and using a ProxyFactoryBean
.
<bean id="helloMatch" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <bean class="org.springaop.Hello"/> </property> <property name="interceptorNames"> <list> <idref bean="helloBeforeAdvice"/> <idref bean="helloAfterRetuningAdvicee"/> </list> </property> </bean> <bean id="helloBeforeAdvice" class="org.springaop.advice.BeforeAdvice"/> <bean id="helloAfterRetuningAdvice" class="org.springaop.advice.AfterRetuningAdvice"/>
In the configuration of the example, we can see how a helloMatch
is defined, which is in fact a ProxyFactoryBean
that puts together the target object on which the crosscutting concern has to be applied, and the list of advice that contains the crosscutting functionalities. In this case, two of the advices are used in the programmatic modality and are applied at the target object as a reference in the list of interceptors that can be applied on the object.
It is an implementation of Spring FactoryBean
that allows you to specify a bean to target and provides a set of advice and advisors for that bean that are eventually merged into an AOP proxy. Because you can use both advisor and advice with the ProxyFactoryBean
, you can configure not only the advice declaratively, but the pointcuts as well.
In both modalities Spring uses internally two sorts of proxy: JDK proxy or CGLIB proxy.
The concept of an Advisor principally concerns Spring 1.x. We will see later how it can benefit by the syntax of the pointcuts of AspectJ.
From the 2.x version onwards, there is a closer integration or configuration based on AspectJ and its syntax, either through annotations or through schema-based configuration. In any case, it's always possible to use AOP in the classic way as in the 1.x version.
Spring is first of all an IoC
Container, and so it allows using the components that implement the AOP as a simple bean, assembling them and obtaining the result of the AOP weaver through Proxy classes, as we previously described.
Instead, AspectJ provides a static implementation of AOP that is produced at compile time. Spring provides a dynamic implementation of AOP, as it is implemented through the creation and the use of proxy classes that permit the implementation of a chain of interceptors.
Obviously, a static implementation provides better performance, but requires greater knowledge and a compiler, whereas the dynamic implementation is easier to use and more accessible. It can be disabled from configuration and never requires anything different from the usual Java compiler.
Spring permits only method execution to be used as a joinpoint. So we can't use with Spring AOP all the features of AOP, but we can do so with AspectJ called by Spring. For example, we must use the support of AspectJ if we want to use as a joinpoint:
The invocations of constructors
Access to the domains of objects with the setter and getter
The initialization of an object
The initialization of an object with a calling
super()
The execution inside a class with
this()
The calling of a method
Therefore, the aspect can be normal Java classes with the annotation @Aspect
, or configured using configuration XML.
The advices are seen as interceptors that maintain a chain of interceptors around the joinpoint.
Pointcuts are performed according to the matching of the AspectJ pointcut expression language, or according to regular expressions following the rules present since Spring 1.x.
This introduction clearly shows that Spring has a simplified pattern of the whole set of AOP features so that AOP can be used with no special editing and alterations to the bytecode. This would be necessary together with the use of AspectJ, if we wanted all the features of AOP as we will see in the rest of this book.