SpringMVC拦截器详解

拦截器是每个Web框架必备的功能,也是个老生常谈的主题了。

本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理。

重要接口及类介绍

1. HandlerExecutionChain类

  由HandlerMethod和Interceptor集合组成的类,会被HandlerMapping接口的getHandler方法获取。

2. HandlerInterceptor接口

  

  SpringMVC拦截器基础接口。 

3. AbstractHandlerMapping

  HandlerMapping的基础抽象类。

4. AsyncHandlerInterceptor

  继承HandlerInterceptor的接口,额外提供了afterConcurrentHandlingStarted方法,该方法是用来处理异步请求。当Controller中有异步请求方法的时候会触发该方法。 楼主做过测试,异步请求先支持preHandle、然后执行afterConcurrentHandlingStarted。异步线程完成之后执行preHandle、postHandle、afterCompletion。 有兴趣的读者可自行研究。

5. HandlerInterceptorAdapter

  实现AsyncHandlerInterceptor接口的抽象类,一般我们使用拦截器的话都会继承这个类。然后复写相应的方法。

6. WebRequestInterceptor

  与HandlerInterceptor接口类似,区别是WebRequestInterceptor的preHandle没有返回值。还有WebRequestInterceptor是针对请求的,接口方法参数中没有response。

  

  AbstractHandlerMapping内部的interceptors是个Object类型集合。处理的时候判断为MappedInterceptor[加入到mappedInterceptors集合中];HandlerInterceptor、WebRequestInterceptor(适配成WebRequestHandlerInterceptorAdapter)[加入到adaptedInterceptors中]

7. MappedInterceptor

  一个包括includePatterns和excludePatterns字符串集合并带有HandlerInterceptor的类。 很明显,就是对于某些地址做特殊包括和排除的拦截器。

  

8. ConversionServiceExposingInterceptor

  默认的<annotation-driven/>标签初始化的时候会初始化ConversionServiceExposingInterceptor这个拦截器,并被当做构造方法的参数来构造MappedInterceptor。之后会被加入到AbstractHandlerMapping的mappedInterceptors集合中。该拦截器会在每个请求之前往request中丢入ConversionService。主要用于spring:eval标签的使用。

源码分析

首先我们看下拦截器的如何被调用的。

Web请求被DispatcherServlet截获后,会调用DispatcherServlet的doDispatcher方法。

很明显地看到,在HandlerAdapter处理之后,以及处理完成之后会调用HandlerExecutionChain的方法。

HandlerExecutionChain的applyPreHandle、applyPostHandle、triggerAfterCompletion方法如下:

很明显,就是调用内部实现HandlerInterceptor该接口集合的各个对应方法。

下面我们看下HandlerExecutionChain的构造过程。

HandlerExecutionChain是从HandlerMapping接口的getHandler方法获取的。

HandlerMapping的基础抽象类AbstractHandlerMapping中:

我们看到,HandlerExecutionChain的拦截器是从AbstractHandlerMapping中的adaptedInterceptors和mappedInterceptors属性中获取的。

拦截器的配置

清楚了HandlerExecutionChain的拦截器属性如何构造之后,下面来看下SpringMVC是如何配置拦截器的。

1. *-dispatcher.xml配置文件中添加 <mvc:interceptors>配置

<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:exclude-mapping path="/login"/>   
    <mvc:exclude-mapping path="/index"/>
    <bean class="package.interceptor.XXInterceptor"/>
  </mvc:interceptor>
</mvc:interceptors>

这里配置的每个<mvc:interceptor>都会被解析成MappedInterceptor。

其中子标签<mvc:mapping path="/**"/>会被解析成MappedInterceptor的includePatterns属性;<mvc:exclude-mapping path="/**"/>会被解析成MappedInterceptor的excludePatterns属性;<bean/>会被解析成MappedInterceptor的interceptor属性。

<mvc:interceptors>这个标签是被InterceptorsBeanDefinitionParser类解析。

2. 配置RequestMappingHandlerMapping,并配置该bean对应的interceptors集合属性。 这里的interceptors集合是个Object类型的泛型集合。

  AbstractHandlerMapping抽象类只暴露了1个拦截器的set方法 -> interceptors。

  adaptedInterceptors和mappedInterceptors均没有暴露set方法,因此我们只能为RequestMappingHandlerMapping配置interceptors属性。

  其实AbstractHandlerMapping内部的initInterceptors方法中,会遍历interceptors集合,然后判断各个项是否是MappedInterceptor、HandlerInterceptor、WebRequestInterceptor。

  其中MappedInterceptor类型的拦截器会被加到mappedInterceptors集合中,HandlerInterceptor类型的会被加到adaptedInterceptors集合中,WebRequestInterceptor类型的会被适配成WebRequestHandlerInterceptorAdapter加到adaptedInterceptors集合中。

  

  

  如果读者配置了:

<mvc:annotation-driven/>

那么配置如下:

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
  <property name="interceptors">
    <bean class="package.interceptor.XXInterceptor"/>
  </property>
  <property name="order" value="-1"/>
</bean>

否则,可以去掉order这个属性的设置。

一般建议使用第一种方法。 

编写自定义的拦截器

public class LoginInterceptor extends HandlerInterceptorAdapter {

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        // 获得请求路径的uri
        String uri = request.getRequestURI();

        // 判断路径是登出还是登录验证,是这两者之一的话执行Controller中定义的方法
        if(uri.endsWith("/login/auth") || uri.endsWith("/login/out")) {
            return true;
        }

        // 进入登录页面,判断session中是否有key,有的话重定向到首页,否则进入登录界面
        if(uri.endsWith("/login/") || uri.endsWith("/login")) {
            if(request.getSession() != null && request.getSession().getAttribute("loginUser") != null) {
                response.sendRedirect(request.getContextPath() + "/index");
            } else {
                return true;
            }
        }

        // 其他情况判断session中是否有key,有的话继续用户的操作
        if(request.getSession() != null && request.getSession().getAttribute("loginUser") != null) {
            return true;
        }

        // 最后的情况就是进入登录页面
        response.sendRedirect(request.getContextPath() + "/login");
        return false;
  }

}

登录Controller:

@Controller
@RequestMapping(value = "/login")
public class LoginController {

    @RequestMapping(value = {"/", ""})
    public String index() {
        return "login";
    }

    @RequestMapping("/auth")
    public String auth(@RequestParam String username, HttpServletRequest req) {
        req.getSession().setAttribute("loginUser", username);
        return "redirect:/index";
    }

    @RequestMapping("/out")
    public String out(HttpServletRequest req) {
        req.getSession().removeAttribute("loginUser");
        return "redirect:/login";
    }

}

*-diapatcher.xml配置:

<mvc:interceptors>
  <mvc:interceptor>
    <mvc:mapping path="/**"/>
    <bean class="org.format.demo.interceptor.LoginInterceptor"/>
  </mvc:interceptor>
</mvc:interceptors>

PS:我们看到LoginInterceptor里的preHandle方法对于地址“/login/auth”和"/login/out"不处理。

因此,可以写点配置,少写带java代码。在拦截器配置中添加2个exclude-mapping,并且去掉LoginInterceptor里的

if(uri.endsWith("/login/auth") || uri.endsWith("/login/out")) {
  return true;
}

配置新增:

<mvc:exclude-mapping path="/login/out"/>
<mvc:exclude-mapping path="/login/auth"/>

总结

总结了SpringMVC拦截器的原理以及各种配置,像网上很多人会问为什么拦截器执行preHandle方法返回false之后还是会执行afterCompletion方法,其实我们看下源码就知道了。

关于异步请求方面的拦截器以及第二种配置方法(interceptors集合属性可加入继承自HandlerInterceptorAdapter抽象类的类以及实现WebRequestInterceptor接口的类),读者可自行研究。

原文:

https://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html

原文地址:https://www.cnblogs.com/winner-0715/p/9749039.html

时间: 2024-10-03 14:45:49

SpringMVC拦截器详解的相关文章

SpringMVC拦截器详解[附带源码分析]

目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 拦截器是每个Web框架必备的功能,也是个老生常谈的主题了. 本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍 1. Hand

[转]SpringMVC拦截器详解[附带源码分析]

目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 拦截器是每个Web框架必备的功能,也是个老生常谈的主题了. 本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍 1. Hand

SpringMVC拦截器详解[附带源码分析]作奏抓诅做准

http://www.ebay.com/cln/7vt_ldrz/2015.01.29/166953650015 http://www.ebay.com/cln/dtr_rfzf/2015.01.29/166836224013 http://www.ebay.com/cln/ldx_nxtv/2015.01.29/166688651010 http://www.ebay.com/cln/b1t_xbzd/2015.01.29/166688653010 http://www.ebay.com/cl

SpringMVC拦截器详解[附带源码分析]拽柞奏租卓咨

http://www.ebay.com/cln/tjp_btlh/2015.01.29/166584777017 http://www.ebay.com/cln/951_thpr/2015.01.29/166964286015 http://www.ebay.com/cln/r5d_nzjp/2015.01.29/166698555010 http://www.ebay.com/cln/lp5_bpxd/2015.01.29/166964288015 http://www.ebay.com/cl

spring--处理器拦截器详解——跟着开涛学SpringMVC

5.1.处理器拦截器简介 Spring Web MVC的处理器拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器) 类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理.   5.1.1.常见应用场景 1.日志记录:记录请求信息的日志,以便进行信息监控.信息统计.计算PV(Page View)等. 2.权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面: 3.性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时

SpringMVC中使用Interceptor拦截器详解

SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理.比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那样子判断当前时间是否是购票时间. 一.定义Interceptor实现类 SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的.在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Intercep

SpringMVC自定义拦截器详解 (转)

经过一天的奋战,终于会使用了SpringMVC,下面讲下他的拦截器.使用SpringMVC拦截器和Struts2一样,Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口.完整的例子可以到我的资源下载:http://download.csdn.net/download/tjcyjd/4255319 HandlerInterceptor接口的代码如下: package org.springfra

struts2内置拦截器和自定义拦截器详解(附源码)

一.Struts2内置拦截器 Struts2中内置类许多的拦截器,它们提供了许多Struts2的核心功能和可选的高级特 性.这些内置的拦截器在struts-default.xml中配置.只有配置了拦截器,拦截器才可以正常的工作和运行.Struts 2已经为您提供丰富多样的,功能齐全的拦截器实现.大家可以至struts2的jar包内的struts-default.xml查看关于默认的拦截器与 拦截器链的配置.内置拦截器虽然在struts2中都定义了,但是并不是都起作用的.因为并不是所有拦截器都被加

Struts2拦截器详解

作者:禅楼望月 1. Struts2内置拦截器 Struts2内置了大量的拦截器,如下图: 图片来自:刘水镜的博客:菜鸟学SSH(四)--Struts2拦截器 这些拦截器以name-class的形式配置在struts-default.xml中: <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/> <interceptor n