Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Tuesday, September 6, 2011

HandlerInterceptors with Spring 2.5+ Annotations

Use the Spring @Component annotation on Interceptor classes to make them available to a request / response chain.

Interceptors are used in many frameworks to insert a processing stage into a chain.  Java EE Servlet Filters, JPA/Hibernate @PreUpdate, and Spring HandlerInterceptors are some examples.  In the case of Spring, a programmer can define a sequence of HandlerInterceptors to be called when an HTTP request comes in, eventually forwarding control and processing to that point to a Controller.

Controller
 
The Controller in Spring MVC with annotations is defined using the @Controller annotation on a Java class.  Each method can be associated with a URL mapping.  This mapping may be relative to the @Controller class or absolute.

@Controller
public class ApplicationController { 

 @RequestMapping(value="/BudgetServlet",params= 
                "BudgetCode","RequestorId"})
 public ModelAndView budget(
      @RequestParam("BudgetCode") String budgetCode, 
      @RequestParam("RequestorId") String requestorId
  ) {
  // retrieve records from the DB
  return new ModelAndView("success");
  }
}

In this case, there is a class ApplicationController that serves as a controller.  There is a method 'budget' which will be invoked when /BudgetServlet?BudgetCode=ACTUAL&RequestorId=FINANCE (a legacy URL) is requested.

Annotations
 
To enable the annotations, make sure that the following is defined in the bean definition file.

<context:component-scan base-package="basicmvc"></context:component-scan>

View Config
 
Also, the bean definition file is where the view technology (JSP, Velocity, Freemarker) is configured.  Here is a sample configuration for standard JSTL.

<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="viewClass"
value="org.springframework.web.servlet.view.JstlView">
   </property>
   <property name="prefix" value="/WEB-INF/jsp/"></property>
   <property name="suffix" value=".jsp"></property>
</bean>


The above configuration will work as a single item of processing, the budget() method in ApplicationController when paired with a JSP "success.jsp".  The name "success" corresponds to the reference in the budget method.

Interceptors
 
To extend this configuration to include an interceptor that will log a message at the start of the request and as the response is prepared, create a class that extends HandlerInterceptorAdapter.  To make the class "Spring-enabled", add the @Component annotation.  Here is an example class.

@Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {

  private Log logger = LogFactory.getLog("logger");

  @Override
  public void postHandle(HttpServletRequest request,
     
HttpServletResponse response, Object handler,
     ModelAndView modelAndView) throws Exception {
   super.postHandle(request, response, handler, modelAndView);
   logger.info("logging after request");
  }

  @Override
  public boolean preHandle(HttpServletRequest request,
    HttpServletResponse response, Object handler) throws Exception {
    boolean ret_val = super.preHandle(request, response, handler);
   logger.info("logging before request");
   return ret_val;

 }
}

@Controller is an extension of @Component that is a special flag to web containers.  Both annotations turn their classes into beans that are able to be for wiring, auto-wiring included.  By default, the names of the beans will be the class name with the first character lower-case.  So the class @Component LoggingInterceptor will register a bean "loggingInterceptor".

The Interceptor bean can then be used in a handlerMapping definition configured through the bean definitions file.

<bean id="handlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="interceptors">
    <list>
    <ref bean="loggingInterceptor" />
    </list>
   </property>
   <property name="mappings">
    <value>
     /BudgetServlet=applicationController
    </value>
   </property>
</bean> 


Behavior
 
So, in this example, a request for /BudgetServlet will make the following calls

  1. loggingInterceptor.preHandle()
  2. applicationController.budget()
  3. loggingInterceptor.postHandle()
Then, control will be forwarded to the view "success.jsp".

Of course it's easy to put a few logging statements in the budget() method, but Interceptors give you a solution that can translate to other contexts.  Interceptors can be turned on for certain mappings and off for others unlike application code which remains unless made conditional by logic.  Testing is easier because dedicated unit tests can focus on the logging services independent of the particular class buried in a business method like budget().

No comments:

Post a Comment