I was teaching another class yesterday and the focus was mostly on the web-tier. This is where we usually spend a lot of time on Spring Web MVC and Spring Web Flow and if the class is interested we also do a bit of Acegi Security (although in this half hour we’re only scratching the surface of it).
One of the subjects in the web module is using HandlerInterceptors to transparently add behavior to web requests. HandlerInterceptors are nice little components that allow you to for example do simple authentication, populate the Log4J Nested Diagnostics Context is populate the model returned by a controller with information each and every view needs (such as user information or information about the current state of the system).
When we touch on HandlerInterceptors, most of the time the audience pretty much sees the resemblance with Servlet Filters immediately. The resemblance is certainly there. There are pretty good reasons why we initially choose for a special purpose interception mechanism (Servlet Filters and EJB 2.x & 1.x transaction and security management are also good examples of special purpose interception mechanisms).
One of the guys in class asked me if it was possible to implement Spring’s HandlerInterceptor mechanism using Spring’s new AOP functionality. Interesting question! Yes in fact it’s possible and that’s what I’ll be reviewing in this post.
The traditional approach (using HandlerInterceptors)
Let’s first review a short example of how we use HandlerInterceptors to transparently do something completely orthogonal to any request in the system.
/**
* Simple handler interceptor that outputs some logging right
* before the request is executed.
*
* @author Alef Arendsen
*/
public class OldSchoolRequestMonitor implements HandlerInterceptor {
public void afterCompletion(HttpServletRequest req,
HttpServletResponse res, Object handler, Exception ex)
throws Exception {
}
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, ModelAndView mav)
throws Exception {
}
/**
* Here we're just implementing the preHandle method
* to output a bit of logging data *before* the request
* is handled.
*/
public boolean preHandle(HttpServletRequest request,
HttpServletResponse arg1, Object response) throws Exception {
System.out.println("Request came in (using a HandlerInterceptor now)");
return true;
}
}
This interceptor is actually pretty straightforward. Plugging it in to let it intercept requests is arguably even simpler. After we’ve created our web application infrastructure (web.xml and ***-servlet.xml file), we’re ready to put it to work:
In short, the process after we’ve added the above code to our ***-servlet.xml file is as follows:
- A request (/helloWorld.html for example) comes in, the DispatcherServlet picks it up
- Using a handler mapping, the DispatcherServlet determines the execution chain of this request (what handlers or controller are there to handle this request, are there any interceptors, et cetera)
- It finds there is an interceptor (the one configured alongside the HandlerMapping) and a controller (the HelloWorldController) that both match
- The DispatcherServlet starts to execute both in turn and that’s how the HandlerInterceptor is allowed to intercept requests
Additional features the HandlerInterceptor class has are the following:
- - it allows modification of the request processing chain by returning true or false from the
preHandle()method. If you returnfalse, the execution will halt - - it allows you to modify the ModelAndView instance returned by controllers in the
postHandle()method, which is pretty neat because you can do all kinds of fancy stuff there - - it allows you to inspect exceptions thrown from the
handleRequest()method implemented by Controllers
Using simple (Spring 2.0) plain object advice
So now for the Spring 2.0 approach with plain objects and AOP. Instead of implementing the HandlerInterceptor interface, we’re going to reuse a simple object that doesn’t have any specific Spring interfaces:
public class RequestMonitor {
public void logRequest() {
System.out.println("Request came in (using a simple Spring AOP 2.0 advice now)");
}
}
So the requirement here basically is that we want to log every web request. Let’s express that using the usual (AOP) suspects. First, our pointcut:
Review this pointcut carefully and think what it actually means. How we usually explain things to people is to read the aspect as follows: a web request (notice the name of the pointcut) is the execution of the handleRequest method on any Controller.
Now we need to configure a piece of advice. Since the advice itself is a simple object, the only thing we need to do is configuring it in Spring:
Linking the advice object and the pointcut is a bit more completed, but actually not that difficult either. We want to log incoming requests before the are handled (as stated earlier in the post). So we will be using before advice. We want to call the logRequest() method as seen in the RequestMonitor class defined above. And we want to do that if the webRequest pointcut matches (in other words, for every web request):
The entire AOP configuration needs to be wrapped inside an element, but that’s just details for now. The result of the two different ways of implemented the interceptor is exactly the same, so in other words, yes, implement web-level interceptor using AOP is perfectly possible.
Somewhat more advanced advice
Of course I hear everybody saying right now: “so what about access to the request, the response, the model, et cetera)!”. Well, that’s something I won’t be able to cover in this entry, as I’m almost out of time. I’ll post a more advanced sample later this week or next week.
Conclusion
So yes, it’s possible to get basic HandlerInterceptor behavior in place with Spring AOP as well. The question of course still remains if all of the things we can do with a HandlerInterceptor are also possible using Spring AOP. We’ll see that in my next entry. We’ll also touch on the subject of whether or not this is a good thing to do.
[i] requestmonitor-1.0.zip – the sample application showing both ways of monitoring web requests (note that for the sample to work, you need to put the Spring 2 jar in the lib directory as well as the AspectJ Weaver jar. I’ve left those out, otherwise the file size would grow too big.





