前言

SpringMVC是目前主流的Web MVC框架之一。

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

默认的标签初始化的时候会初始化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属性;会被解析成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这个属性的设置。

为什么呢?请参考楼主的另外一篇博客:http://www.cnblogs.com/fangjian0423/p/spring-Ordered-interface.html

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

编写自定义的拦截器

public class LoginInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {// 获得请求路径的uriString 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接口的类),读者可自行研究。

面试官:给我说一下 Spring MVC 拦截器的原理?相关推荐

  1. 面试:给我说一下Spring MVC拦截器的原理?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://urlify.cn/namaQ ...

  2. java面试 拦截器问题_面试必问:给我说一下Spring MVC拦截器的原理?

    拦截器是每个Web框架必备的功能,也是个老生常谈的主题了.本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍1. HandlerExecutionCh ...

  3. 使用session监听+spring MVC拦截器禁止用户重复登录

    在许多web项目中,需要禁止用户重复登录.一般来说有两种做法: 一是在用户表中维护一个字段isOnLine(是否在线),用户登录时,设定值为true,用户退出时设定为false,在重复登录时,检索到该 ...

  4. spring mvc拦截器_Spring MVC拦截器示例

    spring mvc拦截器 我认为现在是时候看看Spring的MVC拦截器机制了,这种机制已经存在了很多年,并且是一个非常有用的工具. Spring Interceptor会按照提示说:在传入的HTT ...

  5. Spring MVC拦截器~~~登陆验证拦截

    [ 30 分 钟 轻 松 入 门 Spring MVC][web 三 大 组 件 之 ~ ~ Filter 过 滤 器] Interceptor 拦截器学习: 1.了解spring mvc拦截器的概念 ...

  6. 【Java Web开发学习】Spring MVC 拦截器HandlerInterceptor

    [Java Web开发学习]Spring MVC 拦截器HandlerInterceptor 转载:https://www.cnblogs.com/yangchongxing/p/9324119.ht ...

  7. Java Spring MVC框架 VIII 之 Spring MVC拦截器

    Java Spring MVC框架 VIII 之 Spring MVC拦截器 Spring MVC拦截器 1.拦截器简介 拦截器是SpringMvc框架提供的功能 它可以在控制器方法运行之前或运行之后 ...

  8. spring mvc 拦截器拦截jsp页面

    spring mvc 拦截器怎么拦截jsp页面 你这个 是拦截带 /jsp 的 .do请求 解决方案 用spring 的拦截器 去拦截 所有的 .do 请求, 然后写一个 过滤器去拦截 所有的.jsp ...

  9. Spring MVC拦截器

    1.如何实现登录权限检查 使用session进行约定值的检查 实现方法: (1)使用Filter (2)使用Spring的拦截器 拦截器组件简介:Spring MVC特有组件,可以在调用Control ...

最新文章

  1. 深入了解 Oracle Flex ASM 及其优点
  2. 传统生成API文档弊端
  3. python笔记之matplotlib.pyplot曲线平滑自定义函数:smooth_curv()
  4. 服务器组件架构,tomcat组件图解 一个web服务器的架构演化史
  5. DVD-Cloner 2022 for mac(DVD光盘刻录工具)
  6. IDEA好看的主题安装
  7. uniapp分销商城源码开发
  8. python遍历列表中所有元素_python如何遍历列表所有元素?
  9. PowerApps 的最近更新
  10. cdn刷新api_【第1868期】闲话 CDN
  11. 计算机模拟水循环的过程,“SWAT主要作物水循环模拟方法详解”的学习及心得...
  12. 主板电源开关接口图解_图解:主板电线接法(电源开关、重启等)
  13. 百度收录如何API提交(java、python)
  14. MTK6577+Android之TP(触摸屏)
  15. 云计算机账号能锁定一个电脑吗,使用云电脑时我们的账号是否安全?会不会被盗号?...
  16. Java学习--day02---运算,一些重要的程序
  17. 还原系统无法重启计算机的快捷键,电脑怎么从bios还原系统
  18. Rhino6安装Weaverbird
  19. Python Django,静态资源托管
  20. Linux线程条件控制实现线程的同步pthread_cond_init、pthread_cond_destroy、pthread_cond_wait、pthread_cond_signal

热门文章

  1. HTML 5新元素和CSS
  2. python爬虫——论抓包的正确姿势和学好Javascript的重要性(1)
  3. jQuery滑动效果实例
  4. 《数据分析变革:大数据时代精准决策之道》一第一部分 变革已然开始
  5. 渐统江湖的项目原型生成工具 -- Maven Archetype
  6. asp.net MVC iis6 虚拟主机兼容开发方式
  7. DBController心得之一:利用DMO对象对SQL2005数据库进行Backup和restore的操作
  8. matlab 二维线图绘制函数 plot用法参数
  9. 双网卡服务器SOCKET编程指定客户端通信网卡
  10. 机器学习入门学习笔记:(3.1)决策树算法