面试官:给我说一下 Spring MVC 拦截器的原理?
前言
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 拦截器的原理?相关推荐
- 面试:给我说一下Spring MVC拦截器的原理?
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | https://urlify.cn/namaQ ...
- java面试 拦截器问题_面试必问:给我说一下Spring MVC拦截器的原理?
拦截器是每个Web框架必备的功能,也是个老生常谈的主题了.本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍1. HandlerExecutionCh ...
- 使用session监听+spring MVC拦截器禁止用户重复登录
在许多web项目中,需要禁止用户重复登录.一般来说有两种做法: 一是在用户表中维护一个字段isOnLine(是否在线),用户登录时,设定值为true,用户退出时设定为false,在重复登录时,检索到该 ...
- spring mvc拦截器_Spring MVC拦截器示例
spring mvc拦截器 我认为现在是时候看看Spring的MVC拦截器机制了,这种机制已经存在了很多年,并且是一个非常有用的工具. Spring Interceptor会按照提示说:在传入的HTT ...
- Spring MVC拦截器~~~登陆验证拦截
[ 30 分 钟 轻 松 入 门 Spring MVC][web 三 大 组 件 之 ~ ~ Filter 过 滤 器] Interceptor 拦截器学习: 1.了解spring mvc拦截器的概念 ...
- 【Java Web开发学习】Spring MVC 拦截器HandlerInterceptor
[Java Web开发学习]Spring MVC 拦截器HandlerInterceptor 转载:https://www.cnblogs.com/yangchongxing/p/9324119.ht ...
- Java Spring MVC框架 VIII 之 Spring MVC拦截器
Java Spring MVC框架 VIII 之 Spring MVC拦截器 Spring MVC拦截器 1.拦截器简介 拦截器是SpringMvc框架提供的功能 它可以在控制器方法运行之前或运行之后 ...
- spring mvc 拦截器拦截jsp页面
spring mvc 拦截器怎么拦截jsp页面 你这个 是拦截带 /jsp 的 .do请求 解决方案 用spring 的拦截器 去拦截 所有的 .do 请求, 然后写一个 过滤器去拦截 所有的.jsp ...
- Spring MVC拦截器
1.如何实现登录权限检查 使用session进行约定值的检查 实现方法: (1)使用Filter (2)使用Spring的拦截器 拦截器组件简介:Spring MVC特有组件,可以在调用Control ...
最新文章
- 深入了解 Oracle Flex ASM 及其优点
- 传统生成API文档弊端
- python笔记之matplotlib.pyplot曲线平滑自定义函数:smooth_curv()
- 服务器组件架构,tomcat组件图解 一个web服务器的架构演化史
- DVD-Cloner 2022 for mac(DVD光盘刻录工具)
- IDEA好看的主题安装
- uniapp分销商城源码开发
- python遍历列表中所有元素_python如何遍历列表所有元素?
- PowerApps 的最近更新
- cdn刷新api_【第1868期】闲话 CDN
- 计算机模拟水循环的过程,“SWAT主要作物水循环模拟方法详解”的学习及心得...
- 主板电源开关接口图解_图解:主板电线接法(电源开关、重启等)
- 百度收录如何API提交(java、python)
- MTK6577+Android之TP(触摸屏)
- 云计算机账号能锁定一个电脑吗,使用云电脑时我们的账号是否安全?会不会被盗号?...
- Java学习--day02---运算,一些重要的程序
- 还原系统无法重启计算机的快捷键,电脑怎么从bios还原系统
- Rhino6安装Weaverbird
- Python Django,静态资源托管
- Linux线程条件控制实现线程的同步pthread_cond_init、pthread_cond_destroy、pthread_cond_wait、pthread_cond_signal