欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-12 17:27:26
...

前言:

一、总共有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的注解功能的学习基础上

http://josh-persistence.iteye.com/admin/blogs/1873733 接着学习Spring拦截器功能。

学过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中。可参照我的另一篇博客:

http://josh-persistence.iteye.com/admin/blogs/1874906

将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的启动时加载的属性类似。