Sunday, October 4, 2009

HandlerInterceptors in Spring Web MVC Framework

Last week, I had a wonderful group of people in my class to learn the Spring Framework. In this group, one of my students – Eric - asked me about Spring Web MVC Interceptors. In particular, Eric had a great question about interceptors versus aspect-oriented programming (AOP) provided in Spring. Were they different? Could "normal" Spring AOP be used in place of interceptors and vice versa?

In general, Interceptors, or more precisely HandlerInterceptors, are used to provide cross cutting concerns to a Spring MVC Web application. Here is a quote from Rod Johnson et al's book Java Development with the Spring Framework: "HandlerInterceptors provide the capability to intercept incoming HTTP requests. Interceptors are useful to add additional crosscutting behavior to your web infrastructure, such as security, logging, auditing." Of course, you'll find "crosscutting behavior" the purpose of AOP as well. So indeed, HandlerInterceptors and AOP serve a similar purpose; that is they serve to collect cross cutting concern code usually sprinkled throughout the core concerns of an application. AOP provides a framework for providing cross-cutting concerns throughout all types of applications and application components. HandlerInterceptors are an extension point to a Spring MVC framework, and as such, are a means to provide cross-cutting concerns specific to Web applications. These special cross-cutting concern components also have access to the Web request, response and ModelAndView objects; something normal AOP aspects are not provided directly without some work. If you are unfamiliar with AOP, the Spring API documentation also indicates that HandlerInterceptors work like Servlet Filters.

While there is a rather large amount of documentation and tutorials on intertwining AOP into a Spring application, there is not as much on HandlerInterceptors. So let me use this blog entry to show you how HandlerInterceptors work both in Spring 2 and in Spring 2.5 where annotations (rather than XML) are used to specify the controller and request mapping.

HandlerInterceptors must implement the org.springframework.web.servlet.HandlerInterceptor interface. This interface defines three methods (preHandle, postHandle, and afterCompletion) that get called before a handler is executed (#1 in the diagram below), after a handler is executed but before the view is rendered (#2 below), and after completely handling a Web request and rendering the view(#3 below). These methods allow for all sorts of pre and post processing in the chain of execution associated to each and every Web request.



Here is a simple implementation of the HandlerInterceptor.

package com.intertech.controllers;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.HandlerInterceptor;
public class
TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("In pre-processing/n");
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("done with request/n");
}
@Override
public void postHandle(HttpServletRequest request, HtpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("In post-processing/n");
}
}

Note that the preHandle method returns a boolean. If false is returned, the interceptor indicates the normal execution chain should be aborted. The result is to send an HTTP error or other custom response to the user. The postHandle method is provided a copy of the ModelAndView object. This allows the interceptor to modify the model information or view displayed as normally determined by the Spring MVC controller. Finally, the afterCompletion method serves as a kind of callback after processing the request and rendering the view. This method can and should clean up any resources used by the interceptor.

In Spring 2, interceptors are woven into the Web request execution chain through XML configuration of the handler mapping (note the interceptors property for the SimpleUrlHandlerMapping example below).
<bean id="testInterceptor" class="com.intertech.controllers.TestInterceptor" />
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="testInterceptor"/>
</list>
</property>
<property name="mappings">
<props>
<prop key="/addcontact.request">addContactController
</prop>
<prop key="/displayContacts.request">manageContactsController
</prop>
<prop key="/deleteContact.request">manageContactsController
</prop>
</props>
</property>
</bean>
In Spring 2.5 and better, since handler mapping is handled via @RequestMapping annotations, the interceptor must be added to the DefaultAnnotationHandlerMapping. In Spring 2.5 and beyond, the DispatcherServlet enables the DefaultAnnotationHandlerMapping by default, which looks for @RequestMapping annotations on @Controllers. However, the DefaultAnnotationHandlerMapping can still be added to the Spring configuration file and the interceptors added to this handler mapping as shown below.

<bean id="testInterceptor" class="com.intertech.controllers.TestInterceptor" />
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list><ref bean="testInterceptor"/></list>
</property>
</bean>
Spring already comes with several interceptor adapters (HandlerInterceptorAdapter, LocaleChangeInterceptor, ThemeChangeInterceptor, UserRoleAuthorizationInterceptor, WebContentInterceptor, WebRequestHandlerInterceptorAdapter) so that developers don't have to implement common Web application cross cutting concerns. For example, the UserRoleAuthorizationInterceptor checks the authorization of the current user against the Java EE security roles, as evaluated by HttpServletRequest's isUserInRole method.

To learn more about the Spring Framework or Spring Web MVC, please consider joining me for Intertech's Complete Spring Core or Complete Spring Web class. We offer training at our facility as well as virtually right to your desktop wherever you are.

1 comment:

  1. Nice Explanation. HandlerInterceptor can also be used in java configuration instead of XML.

    @Configuration
    public class AppConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new TransactionInterceptor()).addPathPatterns("/person/save/*");
    }
    }

    Find the detail.




    ReplyDelete