Spring3.X @MVC - (三)Spring3中的拦截器

burning 2013-05-30

前言:

一、总共有10节,也就是10篇博客来讲述Spring的MVC,几乎涵盖了所有Spring MVC中的内容。

二、我创建的例子是一个球场预订系统,例子我已经测试调试通过,是一个Maven的project,包含一个Parent project:wsheng-spring-base和一个子Module:wsheng-spring-mvc.

三、在Eclipse中直接import maven的project即可,会同时引入上诉两个project的。

四、如果你没有耐心,可以不必往下学习,因为网上有很多例子,但是都是讲的Spring MVC很少的面,而且你可以快速的上手,但如果你想真正了解Spring MVC中的很多细节,就可以慢慢的去看博客(从第一节到第十节),如果有什么问题,欢迎信息告诉我。

五、学习的方法是你可以先将源码导入到eclipse中,然后根据博客上的内容,对照源码,慢慢消化,这是个漫长的过程,但是会帮助你了解很多Spring MVC的细节。

===================================================================================

用Spring处理程序拦截器拦截请求

在Spring3.X @MVC - (二)So Easy的注解功能的学习基础上

学过Java Web编程的开发人员都知道,Servlet API中有Servlet过滤器,该过滤器可以可以在Servlet进行Web请求先后进行相关的处理。其实Spring中也可以实现类似而且更加强大的功能。可以在Spring Web上下文中配置和过滤器相似的功能部件,这样就可以使用Spring强大的容器特性。

SpringMVC中可以使用拦截器(Handler Interceptors)拦截web请求进行预先和事后的处理。每个程序拦截器都必须实现HandlerInterceptor接口,实现该接口必须实现3个方法:preHandler(), postHandle(),afterCompletion()

preHandle(): 处理Request之前执行。

postHandler():处理Request方法之后但是还没有返回View之前执行,并允许处理ModelAndView对象。

afterCompletion():在所有请求处理完成之后(也就是显示视图之后)调用。

实战和原理:

需求: 测试每个Web请求的处理时间,并由视图将这个时间显示给用户。

package com.wsheng.spring.web;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**

 * 

 * @author Wang Sheng(Josh)

 *

 */

public class MeasurementInterceptor extends HandlerInterceptorAdapter {

    public boolean preHandle(HttpServletRequest request,

            HttpServletResponse response, Object handler) throws Exception {

        long startTime = System.currentTimeMillis();

        request.setAttribute("startTime", startTime);

        return true;

    }

    public void postHandle(HttpServletRequest request,

            HttpServletResponse response, Object handler,

  //Model model) throws Exception {

  ModelAndView modelAndView) throws Exception {

        long startTime = (Long) request.getAttribute("startTime");

        request.removeAttribute("startTime");

        long endTime = System.currentTimeMillis();

        //model.addAttribute("handlingTime", endTime - startTime);

        System.out.println("handlingTime:" + (endTime - startTime));

        modelAndView.addObject("handlingTime", endTime - startTime);

    }

}

你可能会注意到,这个地方MeasurementInterceptor继承了HandlerInterceptorAdapter,而不是实现了HandlerInterceptor接口,这是因为这个地方如果实现了HandlerInterceptor接口,那么就需要实现该接口中的所有方法,而这个地方我们只是简单的记录一下处理Request的时间,是不需要重写afterCompletion()方法的,最简单的方法是让在程序中让该方法体为空。还有另一种更好的方法是继承抽象类HandlerInterceptorAdapter ,实际上HandlerInterceptorAdpater是实现了HandlerInterceptor接口的,并重写了preHandle(), postHandle()和AfterCompletion()方法,而且这些方法都是具体的public方法,只不过preHandle()方法是返回true,postHandle()和afterCompletion()是空实现。所以用户在继承HandlerInterceptorAdapter后,可以根据自己的需要去进行重写。

接着需要将自定义的拦截器注册到DefaultAnnotationHandlerMapping bean中。这个bean的作用是将拦截器应用到所有以@Controller注解标注的类,意思是说所有的控制器都要受到拦截器的控制。(可以在数组类型的interceptors属性中指定多个拦截器)

<!-- Annotation handlers (Applied by default to ALL @controllers -->

    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">

        <property name="order" value="1"/>

        <!-- Interceptors are applied to all annotated controllers -->

         <property name="interceptors">

            <list>

                <ref bean="measurementInterceptor" />

            </list>

        </property>

    </bean>

然后可以在welcome.jsp中显示这个页面:

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>

<head>

<title><spring:message code="welcome.title" text="Welcome" /></title>

</head>

<body>

<h2><spring:message code="welcome.message" text="Welcome to Court Reservation System" /></h2>

Today is <fmt:formatDate value="${today}" pattern="yyyy-MM-dd" />.

<hr />

Handling time : ${handlingTime} ms

<br />

Locale : ${pageContext.response.locale}

</body>

</html>

DefaultAnnoationHandlerMapping的缺点:

如上所述,DefaultAnnoationHandlerMapping会将其中定义的所有的拦截器分配给所有的@Controller注解定义的类,如果想将不同的拦截器应用于不同的@Controller控制器上DefaultAnnoationHandlerMapping显然不能完成。幸运的是这是一个常见的需求,所以Scott Murphy的Spring-plugins项目就可以完成该功能,该项目允许你使用URL在控制器的基础上应用拦截器。

你可以在http://code.goole.com/p/springplugins/downloads/list中下载该项目,并将相关的jar配置到maven-repository中,但是很不幸的是,截止我发布这篇博客的时候,springplugins.jar还是没有在maven官方的repository中,最简单的方法是将其jar放在应用的WEB-INF/lib中,但是这个显然忽视了Maven强大的功能,比较好的解决办法是将其加到本地的maven repository中。可参照我的另一篇博客:

将jar包加到项目中后,接下来只需要配置一个springplugin中称作SelectedAnnotationHandlerMapping的一个类,并且将其配置和DefaultAnnoationHandlerMapping bean的配置放在一起就可以了。如:

<!-- Interceptors --> 

    <bean id="measurementInterceptor"

        class="com.wsheng.spring.web.MeasurementInterceptor" />

   <bean id="summaryReportInterceptor"

        class="com.wsheng.spring.web.ExtensionInterceptor" />

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">

        <property name="order" value="1"/>

        <!-- Interceptors are applied to all annotated controllers -->

         <property name="interceptors">

            <list>

                <ref bean="measurementInterceptor" />

            </list>

        </property>

    </bean>

    <bean id="publicMapper" class="org.springplugins.web.SelectedAnnotationHandlerMapping">

        <property name="order" value="0" />

<property name="urls">

<list>

             <value>/reservationSummary*</value>

</list>

</property>

<property name="interceptors">

<list>

         <ref bean="summaryReportInterceptor" />

</list>

</property>

    </bean>

上面的配置会产生如下的效果:measurementInterceptor拦截器应用到所有以@Controller注解的控制器,而summaryReportInterceptor拦截器只用到用@Controller注解的,映射到/reservationSummary*URL的那些控制器。order值越小的,处理优先级越高。order值越大的,处理优先级越低。为处理程序拦截器分配order值的过程与当初在web.xml中分配给servlet的启动时加载的属性类似。

相关推荐