[转]SpringMVC拦截器详解[附带源码分析]
目录
- 前言
- 重要接口及类介绍
- 源码分析
- 拦截器的配置
- 编写自定义的拦截器
- 总结
前言
SpringMVC是目前主流的Web MVC框架之一。
如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html
拦截器是每个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集合中。


配置如下:
<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,配成了-1。 为什么呢?
其实是个小知识,与读者分享一下。
Spring提供了一个Order接口,主要是排序用的。

我们这里使用的AbstractHandlerMapping抽象类也实现了这个接口。
DispatcherServlet初始化HandlerMapping集合的代码:

因此,如果读者又配置了:
<mvc:annotation-driven/>

那 么就会有2只HandlerMapping,所以我们配置RequestMappingHandlerMapping的时候设置order为-1,就是为 了让这个HandlerMapping的优先级高于<mvc:annotation-driven/>配置的RequestMappingHandlerMapping级别。
如果读者只配置了RequestMappingHandlerMapping,没配置<mvc:annotation-driven/>。 那么可以忽略这个小知识~~~~~~~~
一般建议使用第一种方法。
编写自定义的拦截器
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接口的类),读者可自行研究。
文中难免有错误,希望读者能够指出来。
[转]SpringMVC拦截器详解[附带源码分析]的更多相关文章
- SpringMVC拦截器详解[附带源码分析]
目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:h ...
- SpringMVC拦截器详解[附带源码分析](转)
本文转自http://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html 感谢作者 目录 前言 重要接口及类介绍 源码分析 拦截器的配置 ...
- SpringMVC异常处理机制详解[附带源码分析]
目录 前言 重要接口和类介绍 HandlerExceptionResolver接口 AbstractHandlerExceptionResolver抽象类 AbstractHandlerMethodE ...
- SpringMVC视图机制详解[附带源码分析]
目录 前言 重要接口和类介绍 源码分析 编码自定义的ViewResolver 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门bl ...
- SpringMVC类型转换、数据绑定详解[附带源码分析]
目录 前言 属性编辑器介绍 重要接口和类介绍 部分类和接口测试 源码分析 编写自定义的属性编辑器 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那 ...
- Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]
目录 前言 现象 源码分析 实战例子 总结 参考资料 前言 今天研究了一下tomcat上web.xml配置文件中url-pattern的问题. 这个问题其实毕业前就困扰着我,当时忙于找工作. 找到工作 ...
- struts2内置拦截器和自定义拦截器详解(附源码)
一.Struts2内置拦截器 Struts2中内置类许多的拦截器,它们提供了许多Struts2的核心功能和可选的高级特 性.这些内置的拦截器在struts-default.xml中配置.只有配置了拦截 ...
- SpringMVC @SessionAttributes 使用详解以及源码分析
@sessionattributes @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Docum ...
- Java SPI机制实战详解及源码分析
背景介绍 提起SPI机制,可能很多人不太熟悉,它是由JDK直接提供的,全称为:Service Provider Interface.而在平时的使用过程中也很少遇到,但如果你阅读一些框架的源码时,会发现 ...
随机推荐
- 【Python学习笔记】Coursera课程《Using Python to Access Web Data 》 密歇根大学 Charles Severance——Week2 Regular Expressions课堂笔记
Coursera课程<Using Python to Access Web Data > 密歇根大学 Charles Severance Week2 Regular Expressions ...
- centos 引导盘
# grub.conf generated by anaconda## Note that you do not have to rerun grub after making changes to ...
- aspxgridview只编辑某一列然后更新
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="IsAllowDeliver ...
- MySQL的数据库引擎的类型(转)
腾讯后台开发电话面试问到数据库引擎选用的问题,这里补习一下. 本文属于转载,原文链接为:http://www.cnblogs.com/xulb597/archive/2012/05/25/251811 ...
- 解决Myeclipse编译不生成.class文件问题
1.Project --> clean... 如果该操作无效,请执行2. 2.Preferences -->Java -->Compliler -->Building --& ...
- 你想了解的轮询、长轮询和websocket都在这里了
日常生活中,有很多需要数据的实时更新,比如群聊信息的实时更新,还有投票系统的实时刷新等 实现的方式有很多种,比如轮询.长轮询.websocket 轮询 轮询是通过设置页面的刷新频率(设置多长时间自动刷 ...
- Longest Palindromic Substring (最长回文字符串)——两种方法还没看,仍需认真看看
Given a string S, find the longest palindromic substring in S. You may assume that the maximum lengt ...
- [转载] Python itertools模块详解
原文在这里,写的很详细,感谢原作者,以下摘录要点. itertools用于高效循环的迭代函数集合. 无限迭代器 迭代器 参数 结果 例子 count() start, [step] start, st ...
- Message Queue中的推与拉(转)
Message Queue的设计和实现(7)http://mp.weixin.qq.com/s/zQdDBAHu1UgJJzxH2eCHgQ 数据发送中的推与拉. 当MQ要把数据给消费者的时候,就涉及 ...
- 走进 MvvmLight for Xamarin.Forms
一.Xamarin.Forms 不使用框架时的绑定 需要注意的是BindingContent,不是DataContent <ContentPage xmlns="http://xama ...