Sei sulla pagina 1di 12

Spring: A Quick Journey Through Spring AOP

One of the strengths of the Spring Framework is its robust and flexible aspect oriented
programming infrastructure. In Spring, AOP is a little different than other AOP
frameworks in that it brings with it a consistent AOP infrastructure that can be applied for
other AOP technical solutions. Out of the gate, Spring supports dynamic-proxy-based
AOP, and CGLib-based AOP. Both of these have their limitations, but can honestly be
used in a very large majority of cases. Spring does have integration support with AspectJ
as well, however, so don't worry. Thankfully, the AspectJ integration is not drastically
different from the regular AOP, which I think shows some of the strengths of Spring's
AOP infrastructure; even an AOP library as different as AspectJ can still be integrated.

General Education

Spring's 'built-in' AOP infrastructure is defined by the org.springframework.aop.*


packages. To understand the packages, you have to have at least some idea of the
concepts of AOP as Spring models these concepts *very* closely in implementation, so
I'll explain them. Now... every article I read on AOP seems to just try to confuse me - and
the vocabulary around AOP even makes it worse. I'm going to do my best to describe
things as simply as possible, so bear with me:

• Aspect - Think of this as the general feature you want to apply globally to your
application (logging, performance monitoring, exception handling, transaction
management, etc).
• Advice - A chunk of code that is invoked during program execution, and is a piece
of the logic for implementing your aspect. This is the first important piece of a
Spring AOP aspect implementation! I like to compare advice implementations to
the decorator pattern. While an advice doesn't necessarily wrap an entire object in
concept, it has the same general effect. We'll learn in a bit that how that advice is
applied is more granular/formal than typically defined in the decorator pattern
however.
• Joinpoint - A *single* location in the code where an advice should be executed
(such as field access, method invocation , constructor invocation, etc.). Spring's
built-in AOP only supports method invocation currently, so joinpoints aren't
particularly important to focus on at this point.
• Pointcut - A pointcut is a set of many joinpoints where an advice should be
executed. So if, in Spring, a joinpoint is always a method invocation, then a
pointcut is just a set of methods that, when called, should have advices invoked
around them. This is the second important pieces of a Spring AOP aspect
implementation!
• Targets/Target Objects - The objects you want to apply an aspect or set of
aspects to!
• Introduction - This is the ability to add methods to an object. This is closely tied
to, and is almost analogous to the term 'mixins'. It's really just a way to make an
object of type A also an object of type B. Introduction in Spring is limited to
interfaces.
Spring and Advice Objects

I like to start with the concept of advice objects, because they are the easiest to bridge to
from a non-AOP way of thinking, because an advice is really very similar to a decorator
(as I mentioned above). Advice implementations in Spring are simply implementations of
the org.aopalliance.intercept.MethodInterceptor interface. Woah! Wait a minute
- that's not a Spring class! Nope - it turns out that Spring's AOP implementation uses a
*standard* AOP API from the AOP Alliance, which you can read more about here . The
MethodInterceptor interface is actually a child of the
org.aopalliance.intercept.Interceptor interface, which is a child of another
interface, org.aopalliance.aop.Advice - Whew!. Remember that I said Spring AOP
only supports method invocation, but that conceptually, an advice could include field
access, constructor invocation and a bunch of other things. That is why Spring's advice
starts at the MethodInterceptor interface, even though there are other interfaces higher
up - because MethodInterceptor is the sub-interface that is designed for method-
invocation style advice and for which Spring has support. The MethodInterceptor
interface is really quite simple:

public interface MethodInterceptor extends Interceptor {


Object invoke(MethodInvocation invocation) throws Throwable;
}

Basically, when you write an advice for intercepting a method, you have to implement
one method - the invoke method, and you are given a MethodInvocation object to
work with. The MethodInvocation object tells us a bunch of stuff about the method that
we're intercepting, and also gives a hook to tell the method to go ahead and run.

Let's jump right in and look at a (very) basic method performance profiling advice:

import org.aopalliance.intercept.*;

public class PerformanceInterceptor implements MethodInterceptor {

public Object invoke(MethodInvocation method) throws Throwable {


long start = System.currentTimeMillis();
try {
Object result = method.proceed();
return result;
}
finally {
long end = System.currentTimeMillis();
long timeMs = end - start;
System.out.println("Method: " + method.toString()
+ " took: " + timeMs +"ms.");
}
}
}

Not too complicated really - just capture the time before the method invocation, tell the
method to go ahead and run, then afterward capture the time again, calculate the
difference, and print it out. Incidentally, Spring already has a better implementation of
this type of interceptor - the
org.springframework.aop.interceptor.PerformanceMonitorInterceptor - there
are actually quite a few useful concrete interceptor implementations in there - check it
out.

Spring has multiple alternatives to the basic MethodInterceptor , however, (which is


referred to in the Spring documentation as an 'around' advice) so that if you want to do
more specific things, you can with less complication - these extensions come in the form
of Spring-specific extensions to the Advice interface (siblings to the
MethodInterceptor interface), and they include:

• org.springframework.aop.MethodBeforeAdvice - Implementations of this


interface have to implement this contract:
• void before(Method method, Object[] args, Object target) throws
Throwable;

You'll notice in this case you aren't given the MethodInvocation object, just the
underlying Method
object - this is because the call to proceed() will be handled by Spring for you;
all you need to do is
do what you need *before* the method is called (as the interface name implies).

• org.springframework.aop.AfterReturningAdvice - This is the pong to the ping


of MethodBeforeAdvice .
This interface's method will be called on the return from the invocation of a
method. The contract looks like this:
• void afterReturning(Object returnValue, Method method, Object[]
args, Object target) throws Throwable;

You'll notice it looks a whole like the before advice, but simply adds the Object's
return value to the
method arguments.

• org.springframework.aop.ThrowsAdvice - This is a strange implementation.


Instead of requiring you
to implement a particular contract, this is simply a 'marker' interface, and expects
you to implement any number
of methods that look like this:
• void afterThrowing([Method], [args], [target], [some type of
throwable] subclass)

Oddly enough, the only mandatory argument is the Throwable sub-type.

Here is a snapshot that generally covers the 'advice' hierarchy:

That's a primer on advice objects. Not too bad so far, right? If you've ever worked with in
reflection in Java, most of this is fairly similar in style.

Spring and Pointcuts

What seperates AOP from object-oriented design patterns such as the decorator pattern, at
least when talking about Spring AOP, is the fact that the *what* is defined seperately
from the *where* (or would it be *whom*?). In other words, the 'advice', which is the
code to be invoked, is disconnected entirely from the particular item it is 'advising' -
which, again, in the Spring case is always a method. In other words, an advice in Spring
doesn't have any association, type binding, dependency, or any other form of direct
awareness of the method it is working with.

Remember that the thing that an advice works with is called a JoinPoint . Our join
points in Spring are always methods, and at runtime resolve to
org.aopalliance.aop.Method objects which have made appearances as method
arguments to our advices above. To solidify the point, org.aopalliance.aop.Method
extends org.aopalliance.aop.JoinPoint .

The reason I am bringing joinpoints back up is because a Pointcut object is all about
defining all of the joinpoints that an advice should be 'applied to'. In Spring terms, a
pointcut defines all of the methods that our interceptor should intercept. Pointcuts in
Spring implement the org.springframework.aop.Pointcut interface and look
something like this:

package org.springframework.aop;
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}

The class filter is a special type of object that describes which classes have potential
joinpoints (read methods) on them. The method matcher simply describes which methods
for a given class are considered valid joinpoints for this pointcut. I don't want to spend
too much time on the pointcut interface, however, because chances are - you won't be
implementing it.

Static vs. Dynamic Method Matching

Spring makes a distinction between static and dynamic method matching. A static method
matching pointcut (a subclass of the
org.springframework.aop.support.StaticMethodMatcherPointcut class) knows
at the time it proxies a target object what methods it considers joinpoints on that object. A
dymamic method matching pointcut (see below for implementation details), on the other
hand must be consulted at *every* method invocation. This is a useful implementation
detail because a static method matcher, while having less flexibility (you can't check the
method invocation arguments), is, by design, much faster, as the check is only performed
once, rather than at every method invocation. Static Matching pointcuts implementations
look something like this:

public class MyBeanPointcut extends StaticMethodMatcherPointcut {


public boolean matches(java.lang.reflect.Method theMethod,
java.lang.Class theClass) {
return (MyBean.class.isAssignableFrom(theClass) &&
theMethod.equals(...));
}
}

In most cases, however, this is irrelevant, because there is a very convenient pair of static
method matchers that cover almost all concerns - the
org.springframework.aop.support.JdkRegexpMethodPointcut and
org.springframework.aop.support.PerlRegexpMethodPointcut classes. You
typically configure these outside of your code (we'll get to configuration in a bit), and
don't have to do any nasty reflection checks as seen above. In addition, there is a simpler
variety of text-based matching called the NameMatchedMethodPointcut - which is
similar to the regex implementations, but only matches exact names.

Dynamic matching pointcut implementations typically look something like this:

public class MyBeanPointcut extends StaticMethodMatcherPointcut {


public boolean matches(java.lang.reflect.Method theMethod,
java.lang.Class theClass, Object[] arguments) {
boolean matches = false;
if(MyBean.class.isAssignableFrom(theClass) &&
theMethod.equals(...)) {
if(arguments[0].equals("Joe Smith")) {
matches = true;
}
}
return matches;
}
}

Due to the nature of dynamic pointcuts, there are no real convenience implementations of
this class. Here is what the pointcut hierarchy generally looks like:

You may have noticed, neither of my pointcut examples made any reference to any
advice (e.g. MethodInterceptor implementation) - remember that I said an advice
knows the *what*, not the *where*? Well, a pointcut is the inverse - it knows the
*where*, not the *what*. Therefore, theoretically, these two components can be
intermingled in different configurations and reused.

Tying Pointcuts with Advisors - PointcutAdvisors

I've gone to great lengths above to clarify that pointcuts don't know about advice objects,
and advice objects don't know about pointcuts. Some object somewhere, however, must
know about both if we are going to have any hope of wrapping our beans in aspects. That
is where implementations of PointcutAdvisor come in. Conceptually speaking, a
PointcutAdvisor is nothing more than a pointcut and an advice object combined -
hence the name. The most basic variety of pointcut advisor is the
org.springframework.aop.support.DefaultPointcutAdvisor class. This is for the
most part just a bean that has two references - something akin to this:

public class DefaultPointcutAdvisor {


private Pointcut pointcut;
private Advice advice;

public Pointcut getPointcut() { return pointcut; }


public Advice getAdvice() { return advice; }
public void setPointcut(Pointcut pc) { pointcut = pc; }
public void setAdvice(Advice a) { advice = a; }

}
Yes, yes, in reality it is more complicated than this - but really that is the bulk of it right
there. Now we are getting to a point where we can define the *what* (the Advice) and the
*where* (the Pointcut) in one discrete location. A basic configuration of this advisor in a
spring bean definition may look something like this:

<!--
A simple name matching pointcut that will consider all methods named
'handleRequestInternal'
as joinpoints
-->
<bean name="controller.handle.pointcut"
class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedName" value="handleRequestInternal"/>
</bean>

<!-- A simple MethodInterceptor style advice object (InterceptorA


implements MethodInterceptor) -->
<bean name="interceptorA"
class="com.javalobby.tnt.spring.aop.InterceptorA" />
<!-- A simple MethodBeforeAdvice style advice object (BeforeAdviceA
implements MethodBeforeAdvice) -->
<bean name="beforeAdviceA"
class="com.javalobby.tnt.spring.aop.BeforeAdviceA"/>

<!-- A pointcut advisor that combines the controller pointcut with


interceptor A -->
<bean name="pointcut.advisor1"
class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="interceptorA"/>
<property name="pointcut" ref="controller.handle.pointcut"/>
</bean>

<!--
An alternative pointcut advisor that combines the before advice with
the controller pointcut
Showing how reuse of pointcuts and advices is possible
-->
<bean name="pointcut.advisor2"
class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="beforeAdviceA"/>
<property name="pointcut" ref="controller.handle.pointcut"/>
</bean>

Hopefully this gives you some idea of the relationship of these three classes. As you can
see, advice objects and pointcuts can be intermixed freely into varying pointcut advisors.

Let's Simplify Things

Ok, now, throw all of that example out of the window. Why? Well, because in reality
while the above example is much more flexible, it is quite verbose as well. Most times
that level of granularity is unnecessary, as we will soon see. Earlier I glossed over the
org.springframework.aop.support.PointcutAdvisor class hierarchy, and just
mentioned the most basic DefaultPointcutAdvisor . Let's take a closer look at the
available class hierarchy now:

Did you notice how similar the names are to the pointcuts we just learned about? Most of
these classes take the seperation of pointcut and advisor out of the equation, which, while
theoretically reducing some degree of flexibility, typically makes configuration much
easier. Here is the same example above, using the NameMatchMethodPointcut Advisor
object (which combines the PointcutAdvisor API with the
NameMatchMethodPointcut class):

<!-- A simple MethodInterceptor style advice object (InterceptorA


implements MethodInterceptor) -->

<bean name="interceptorA"
class="com.javalobby.tnt.spring.aop.InterceptorA" />
<!-- A simple MethodBeforeAdvice style advice object (BeforeAdviceA
implements MethodBeforeAdvice) -->
<bean name="beforeAdviceA"
class="com.javalobby.tnt.spring.aop.BeforeAdviceA"/>

<!--
Use the NameMatchMethod pointcut advisor to make things a little
simpler.
-->
<bean name="pointcut.advisor1"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="interceptorA"/>
<property name="mappedName" value="handleRequestInternal"/>
</bean>

<!--
Use the NameMatchMethod pointcut advisor to make things a little
simpler.
-->
<bean name="pointcut.advisor2"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="beforeAdviceA"/>
<property name="mappedName" value="handleRequestInternal"/>
</bean>
As you can see, we got rid of the configuration of one bean entirely. Technically speaking
we are no longer reusing the pointcut, but since pointcut definitions are typically a
configuration concern (as seen above), it doesn't usually matter one way or another to us.

Gluing it All Together

So, we've covered advice objects and pointcut objects - which when you get down to it,
are the core; the center of Spring AOP support. Then we covered pointcut advisors which
take advice objects and pointcut objects and glue them together into one cohesive chunk -
the peanut butter and jelly; the meat and potatoes; the spaghetti and meatballs; the lamb
and tunafish. There is a very important piece of the puzzle missing however: how do we
wrap these pointcut-advisor combos around our objects? After all, that is the whole point
of all of this. Enter the ProxyFactoryBean .

Without getting into too much detail, Spring supports the concept of a FactoryBean ,
which is a special type of bean, in that the bean returned is a factory result, rather than
just a plain 'newInstance()' call on the class you provided. So, you could have a factory
bean implementation that, based on certain configuration details, factoried different
implementations of a certain class. So, just to solidify my point, in this example:

<bean name="myBean"
class="com.javalobby.tnt.spring.factorybeans.MyFactoryBean"/>

If you ask for myBean from the bean factory, you're *not* going to get an instance of
MyFactoryBean - instead, Spring is going to consult with the MyFactoryBean object,
and ask it to provide the object you want.

Why is all of this important? Well, because this FactoryBean concept is how Spring
wraps your beans - via the some configuration details, and then using some internal tool
(dynamic proxies, CGLib, etc.) to create a proxy for your bean that executes some advice
on method calls when the pointcut says the method is a joinpoint (assuming a pointcut is
defined).

Expanding our example above, here is how Spring's proxy factory bean works:

<!-- A simple MethodInterceptor style advice object (InterceptorA


implements MethodInterceptor) -->
<bean name="interceptorA"
class="com.javalobby.tnt.spring.aop.InterceptorA" />
<!-- A simple MethodBeforeAdvice style advice object (BeforeAdviceA
implements MethodBeforeAdvice) -->
<bean name="beforeAdviceA"
class="com.javalobby.tnt.spring.aop.BeforeAdviceA"/>

<!--
Use the NameMatchMethod pointcut advisor to make things a little
simpler.
-->
<bean name="pointcut.advisor1"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="interceptorA"/>
<property name="mappedName" value="handleRequestInternal"/>
</bean>

<!--
Use the NameMatchMethod pointcut advisor to make things a little
simpler.
-->
<bean name="pointcut.advisor2"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="beforeAdviceA"/>
<property name="mappedName" value="handleRequestInternal"/>
</bean>

<!-- Create our controller bean -->


<bean name="myRawController"
class="com.javalobby.tnt.spring.aop.ExampleController" />

<!-- Create the proxy bean that returns AOP'd varieties of our
controller -->
<bean name="myController"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>pointcut.advisor2</value>
<value>pointcut.advisor1</value>
<value>myRawController</value>
</list>
</property>
</bean>

Notice the interceptorNames property? This is how we tell the proxy factory bean
what advisors or advice objects we want to apply to the proxied bean. Order is important
in this list - the order the advisors or advice objects are entered in the list defines the
order they will be invoked when entering and exiting the bean. Note that in this case the
*last* entry in the list is our bean that we want to be proxied. This is just one possible
way to define the bean to be proxied as we'll see in a moment. Also, did you notice that I
said advisors or advice objects ? That is because the proxy factory bean allows for
another shortcut; not specifying a pointcut at all, just an advice. In those cases, an 'every
method is a joinpoint' style pointcut will automatically be applied. So, if we didn't care
which methods were advised, we could shorten the example above like this:

<!-- A simple MethodInterceptor style advice object (InterceptorA


implements MethodInterceptor) -->
<bean name="interceptorA"
class="com.javalobby.tnt.spring.aop.InterceptorA" />
<!-- A simple MethodBeforeAdvice style advice object (BeforeAdviceA
implements MethodBeforeAdvice) -->
<bean name="beforeAdviceA"
class="com.javalobby.tnt.spring.aop.BeforeAdviceA"/>

<!-- Create our controller bean -->


<bean name="myRawController"
class="com.javalobby.tnt.spring.aop.ExampleController" />

<!-- Create the proxy bean that returns AOP'd varieties of our
controller -->
<bean name="myController"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<list>
<value>beforeAdviceA</value>
<value>interceptorA</value>
<value>myRawController</value>
</list>
</property>
</bean>

Now, I mentioned earlier that there is another way to specify the target bean. This can be
done through the targetName and/or target properties on the factory bean:

<!-- Create the proxy bean that returns AOP'd varieties of our
controller -->
<bean name="myController"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="myRawController"/>
<property name="interceptorNames">
<list>
<value>beforeAdviceA</value>
<value>interceptorA</value>
</list>
</property>
</bean>

If you don't need direct (non-AOP'd) access to your bean, then it may be better for the
simplicity of the file to just use an anonymous inner bean, rather than declaring the bean
seperately to the proxy:

<!-- Create the proxy bean that returns AOP'd varieties of our
controller -->
<bean name="myController"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target"><bean
class="com.javalobby.tnt.aop.ExampleController"/></property>
<property name="interceptorNames">
<list>
<value>beforeAdviceA</value>
<value>interceptorA</value>
</list>
</property>
</bean>

Conclusion for Now


Ok, well, I could keep going on and on and on, but I'd never finish this article, and you'd
never get to read it; and if you *did*, it'd be so long and boring that I might as well put it
on the shelf next to the encyclopedia.

Now, some people who already know about Spring AOP are probably ready to clamor
because I haven't covered these topics (among others I'm sure):

• Introductions/Mixins
• Specialized Proxy Support (such as the TransactionProxyFactoryBean)
• Other corner cases

I didn't cover introductions specifically because they are so uniquely different from other
types of Spring AOP, I thought it would be good to cover the basic, common forms first.
As far as the specialized cases and 'unique tweaks', let's gauge the popularity of the
subject, and see where we get.

Potrebbero piacerti anche