Okay, good you made it past the title. I was at a client that has been a long-time Spring user. Some of the client’s stuff needs a revision and as part of this revision Spring 2.0 AOP was introduced in some places. Today, for the group of developers I shortly touch on some of the basic differences between AOP in Spring 1.x and AOP in Spring 2.0. I figured it would be good to post the examples online.
Let’s first set the stage for the example (and please read this carefully): an exception thrown from a service method should be emailed to an administrator.
Here’s an example of a service method:
package com.mycompany.myapp.service;
public interface DrinkingService {
void doIt();
}
with a corresponding implementation:
package com.mycompany.myapp.service;
public class DrinkingServiceImpl implements DrinkingService {
public void doIt() {
throw new IllegalStateException("I have a hangover so please don't bother me!");
}
}
As you can see, it’s in an illegal state at the moment (probably because the service drank a bit too much Limoncello or something). Normally service wouldn’t do this, but this time of the year (when leaves start falling and days are shortening), I can definitely imagine services grab the bottle.
Anyway, let’s continue. The requirement was to notify an administrator (by email) about any exception occurring inside a service. DrinkingServiceImpl is just one example of a service and because there probably are more services in our application, we’re going to use AOP to implement this specific requirements. Think about the 1:1 principle, separation of concerns (SoC), Don’t Repeat Yourself (DRY) and all that crap…
There are several ways of solving this. Let’s examine them one by one.
Emailing the exception using Spring 1.x
Suppose you’re stuck at Spring 1.2. Then the way to go is using a ProxyFactoryBean. Using this Spring class, you can configure interceptors in order to advise certain methods.
This piece of Spring configuration configures a ProxyFactoryBean and one interceptor, which in this case is a bean called exceptionAdvice. Configuring something like this, obtaining the service bean and calling a method on the service bean, will actually cause the interceptor to ‘run’.
The interceptor (the EmailOnExceptionAdvice) is actually an instance of ThrowsAdvice, which means it only gets called when an exception occurs inside a method call on the drinkingServiceTarget. There’s only one method, which in this case takes an Exception.
public class EmailOnExceptionAdvice implements ThrowsAdvice {
public void afterThrowing(Exception e) {
System.out.println("Email the exception!");
// TODO email the exception to an administrator
}
}
So, one more time:
- The ProxyFactoryBean creates a proxy wrapping the target object (in this case a DrinkingServiceImpl)
- The ProxyFactoryBean also configures an interceptor that acts on any exception occurring inside the service
- The interceptor (advice) receives notice of exceptions occurring and emails them to an administrator
As you can see (run the example if you want more insight), the requirement (exceptions thrown from a service method should be emailed to an administrator) has successfully been implemented!
Note that there’s absolutely no reason to keep yourself from upgrading to Spring 2.0. The new Spring version of in 95% of the cases fully backward-compatible with the 1.x-line and spring-2.0.jar should in most cases be a drop-in replacement for the spring-1.2.7.jar for example.
Emailing the exception using Spring 2.0 and the old advice
You might be wondering why we’ve created Spring 2.0 and enhanced the AOP capabilities so much if things are that easy using Spring 1.x. Well, there actually are a lot of reasons, some of which will probably get clear after I’ve introduced the new approach. Let’s first revisit our initial requirement:
an exception thrown from a service method should be emailed to an administrator.
Although this requirement has been implemented correctly, the way we did it might sound kind of low-level. We used proxy factories, interceptors, advice, target objects and much more, all to email an exception to an administrator. Using Spring 2.0, we’ll see that all these infrastructural components more or less disappear, allowing us to make the code look more like the design while still successfully implementing the requirement.
To not take too big a step, in the new version, we’ll reuse our old advice and we’ll just change the configuration a bit. We’ll start off by defining a pointcut:
What this pointcuts says is the following: a serviceMethod is the execution of any method in service package of my application. That’s what I call ‘make the code look like the design’. The next thing we’ll do is attach the previously created advice to our pointcut and we’re done (this is what we’re doing using an aop:advisor element. The final configuration looks like this (in other words, no more ProxyFactoryBeans).
<code>
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* com.mycompany.myapp.service..*.*(..))" />
<aop:advisor pointcut-ref="serviceMethod" advice-ref="exceptionAdvice" />
</aop:config>
<bean id="exceptionAdvice" class="com.mycompany.myapp.util.EmailOnExceptionAdvice" />
<bean id="service" class="com.mycompany.myapp.service.DrinkingServiceImpl" />
</code>
And now for a completely Spring-less approach
In the previous two solutions, we used special Spring classes to implement our advice. There is a third approach not requiring you to implement any Spring-specific interfaces. This is what we called the ‘POJO advice’ approach. Using the POJO advice approach, you can use any existing class that doesn’t have to have any references to Spring whatsoever to serve as an advice. Okay, let’s change the exception advice to a POJO advice first:
<code>
public class ExceptionEmailer {
public void emailException(Exception exception) {
System.out.println("In Emailer");
// TODO implement emailing functionality
}
}
</code>
This should be straightforward to understand. Upon the catching of an exception the emailException method should be called. Again, we’re going to try to make the code look as much like the design as possible. Here’s how we can do this using Spring AOP in version 2.0:
<code>
<aop:config>
<aop:pointcut id="serviceMethod" expression="execution(* com.mycompany.myapp.service.*.*(..))" />
<aop:aspect ref="exceptionEmailer">
<aop:after-throwing throwing="exception"
pointcut-ref="serviceMethod" method="emailException"/>
</aop:aspect>
</aop:config>
</code>
Let’s have a look at how the aspect reads: after-throwing an exception from any serviceMethod, call the emailException method on the exceptionEmailer. Pretty straightforward if I may say so myself. I can remember Gregor Hohpe telling me once that he tried to code in such as way that he could show his code to non-technical managers. Well, IMO this certainly is a way of doing this!
Other ways of doing this
There are still plenty of other ways of making sure an email is sent after an exception has been raised. One of the ways of using AspectJ, which will result in code resembling the title of this post. Maybe I’ll write about the other approaches some other day. First, I’ll have to write a follow-up to my example about how to use AOP to intercept web requests.
p.s. Here are the sample sources. Just as with the previous post, remember that you should include spring-2.0.jar and aspectjweaver.jar, otherwise things won’t work. I still haven’t found the time to get Maven2 done for these sample projects.