拦截器是每个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

SpringMVC拦截器详解的更多相关文章

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

      目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:ht ...

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

    目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:h ...

  3. SpringMVC拦截器详解[附带源码分析](转)

    本文转自http://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html 感谢作者 目录 前言 重要接口及类介绍 源码分析 拦截器的配置 ...

  4. 通俗易懂之SpringMVC&Struts2前端拦截器详解

    直接进入主题吧!一,配置Struts2的拦截器分两步走1配置对应的拦截器类:2在配置文件Struts.xml中进行配置拦截器同时在Strust2中配置拦截器类有三种方法1实现Interceptor接口 ...

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

    5.1.处理器拦截器简介 Spring Web MVC的处理器拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器) 类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. ...

  6. Struts2 之 modelDriven & prepare 拦截器详解

    struts2 ModelDriven & Prepareable 拦截器 前面对于 Struts2 的开发环境的搭建.配置文件以及 Struts2 的值栈都已经进行过叙述了!这次博文我们讲解 ...

  7. struts2 18拦截器详解(七)

    ChainingInterceptor 该拦截器处于defaultStack第六的位置,其主要功能是复制值栈(ValueStack)中的所有对象的所有属性到当前正在执行的Action中,如果说Valu ...

  8. AspectCore动态代理中的拦截器详解(一)

    前言 在上一篇文章使用AspectCore动态代理中,简单说明了AspectCore.DynamicProxy的使用方式,由于介绍的比较浅显,也有不少同学留言询问拦截器的配置,那么在这篇文章中,我们来 ...

  9. struts2 18拦截器详解(十)

    ModelDrivenInterceptor 该拦截器处于defaultStack中的第九的位置,在ScopedModelDrivenInterceptor拦截器之后,要使该拦截器有效的话,Actio ...

随机推荐

  1. OneNET麒麟座应用开发之四:数据上传测试

    已经测试过OneNET麒麟座开发板了,这次来尝试与OneNET的连接和数据上传.这也是我们测试这块开发办的主要原因,因为在十几种我们有这种将分散的采集点数据上传到后台的需求. 先看看麒麟座这块开发板, ...

  2. SpringMVC(4.1):Controller接口控制器详解(1)

    原文出处: 张开涛 4.1.Controller简介 Controller控制器,是MVC中的部分C,为什么是部分呢?因为此处的控制器主要负责功能处理部分: 1.收集.验证请求参数并绑定到命令对象: ...

  3. LeetCode(47):全排列 II

    Medium! 题目描述: 给定一个可包含重复数字的序列,返回所有不重复的全排列. 示例: 输入: [1,1,2] 输出: [ [1,1,2], [1,2,1], [2,1,1] ] 解题思路: 这道 ...

  4. hdu6153 poj3336强化版kmp+线性dp

    发现很早以前用exkmp做过一次,但是对这题来说只要将两个串翻转一下即可转换成s2的所有前缀出现的问题 /* 给出s1,s2,求s2的每个后缀在s1中出现的次数 ans = sum{后缀长度*出现次数 ...

  5. python 全栈开发,Day91(Vue实例的生命周期,组件间通信之中央事件总线bus,Vue Router,vue-cli 工具)

    昨日内容回顾 0. 组件注意事项!!! data属性必须是一个函数! 1. 注册全局组件 Vue.component('组件名',{ template: `` }) var app = new Vue ...

  6. python 全栈开发,Day68(Django的路由控制)

    昨日内容回顾 1 MVC和MTV MTV 路由控制层(分发哪一个路径由哪一个视图函数处理) V : views (逻辑处理) T : templates (存放html文件) M : model (与 ...

  7. day5模块学习--re正则模块

    1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分.正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十 ...

  8. linux SSH免密码登录远程服务器

    背景:无密码登录,所谓的无密码登录其实是指通过证书认证的方式登录,使用一种‘公私钥’认证的方式来进行ssh登录 在linux系统中,ssh是远程登录的默认工具,因为该工具的协议使用了RSA/DSA的加 ...

  9. python爬虫之反爬虫(随机user-agent,获取代理ip,检测代理ip可用性)

    python爬虫之反爬虫(随机user-agent,获取代理ip,检测代理ip可用性) 目录 随机User-Agent 获取代理ip 检测代理ip可用性 随机User-Agent fake_usera ...

  10. A+B Problem(再升级)

    洛谷P1832 A+B Problem(再升级) ·给定一个正整数n,求将其分解成若干个素数之和的方案总数. 先说我的垃圾思路,根本没有验证它的正确性就xjb写的,过了垃圾样例,还水了20分,笑哭.. ...