SpringMVC源码阅读:拦截器
1.前言
SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧
本文将通过源码(基于Spring4.3.7)分析,弄清楚SpringMVC拦截器的工作原理并自定义拦截器
2.源码分析
进入SpringMVC核心类DispatcherServlet的doDispatch方法,在SpringMVC源码阅读:核心分发器DispatcherServlet曾经分析过,这里再分析一遍

936行获得HandlerExecutionChain,含有HandlerMethod和interceptorList
943行根据HandlerExecutionChain获取RequestMappingHandlerAdapter

958行如果HandlerExecutionChain需要执行下一个拦截器,则返回True
963HandlerAdapter调用Handler处理请求,可以看到,请求方法夹在applyPreHandle和applyPostHandle之间
980行processDispatchResult会调用triggerAfterCompletion,不抛出异常
983和986行调用triggerAfterCompletion会抛出异常
重点看下936行getHandler方法,点进去

1156行HandlerMapping调用getHandler方法获取HandlerExecutionChain
对着getHandler ctrl+alt+b跳转到HandlerMapping接口方法实现处,在AbstractHandlerMapping类中

352行获取HandlerMethod
365行获取HandlerExecutionChain
366行对跨域请求进行拦截处理
点进去365行的getHandlerExecutionChain方法

417行获取requestmapping请求路径
419行判断HandlerInterceptor是不是MappedInterceptor类型,不是则直接向HandlerExecutionChain加入HandlerInterceptor
HandlerInterceptor是MappedInterceptor类型,需要检验是否匹配,最后向HandlerExecutionChain加入HandlerInterceptor
getCorsHandlerExecutionChain方法获取跨域的HandlerExecutionChain和getHandlerExecutionChain同理,园友可自行分析
现在看看HandlerExecutionChain

主要看applyPostHandle、applyPreHandle和triggerAfterCompletion方法
打开applyPostHandle方法

130行接收HandlerInterceptor数组
134行HandlerInterceptor的preHandle方法执行失败依然会执行HandlerExecutionChain的triggerAfterCompletion方法 
triggerAfterCompletion方法在所有拦截器preHandle方法成功执行返回True后才会执行(触发afterCompletion)

3.实例
设置自定义拦截器,继承HandlerInterceptorAdapter

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/login");
return false;
}
}
测试Controller
@Controller
@RequestMapping(value = "/login")
public class LoginController { //@RequestMapping(value = {"/", ""})
@RequestMapping(value = {"login"})
@ResponseBody
public String login() {
return "login";
} @RequestMapping("/auth")
public String auth(@RequestParam String username, HttpServletRequest req) {
req.getSession().setAttribute("loginUser", username);
return "redirect:/";
} @RequestMapping("/out")
public String out(HttpServletRequest req) {
req.getSession().removeAttribute("loginUser");
return "redirect:/login/login";
} }
在dispatcher-servlet.xml配置拦截器
因为我们使用了<mvc:annotation-driven/>注解,SpringMVC源码阅读:Json,Xml自动转换提到过
<mvc:annotation-driven/>自动帮我们注册了
- RequestMappingHandlerMapping
- RequestMappingHandlerAdapter
- ExceptionHandlerExceptionResolver
所以只要在RequestMappingHandlerMapping中配置interceptors属性

interceptors属性来自于RequestMappingHandlerMapping的父类AbstractHandlerMapping
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<bean class="org.format.demo.interceptor.LoginInterceptor"/>
</property>
</bean>
现在浏览器输入任何路径,都会跳转到http://localhost:8080/springmvcdemo/login/login,说明拦截器已经生效
浏览器输入http://localhost:8080/springmvcdemo/login/auth?username=ss,给HttpSession设置Attribute,返回主界面
浏览器输入http://localhost:8080/springmvcdemo/login/out,将HttpSession的Attribute移除,重定向到http://host:port/contextPath/login/login
我们还可以通过<mvc:interceptors>标签来配置拦截器,此时不需要再配置RequestMappingHandlerMapping的interceptors属性
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login/out"/>
<mvc:exclude-mapping path="/login/auth"/>
<bean class="org.format.demo.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
LoginInterceptor这段代码和mvc:exclude-mapping功能一致,是指不拦截的请求路径,可以注释掉

运行效果和刚才一致
讲一下为什么可以这么配置拦截器,mvc:interceptors由InterceptorsBeanDefinitionParser解析,该类实现了BeanDefinitionParser,我在SpringMVC源码阅读:Json,Xml自动转换分析过mvc:annotation-driven如何由AnnotationDrivenBeanDefinitionParser解析,道理是类似的,
核心方法是parse,在InterceptorsBeanDefinitionParser的parse方法打断点验证一下

和我们用mvc:interceptors标签配置的内容一致
4.总结
HandlerExecutionChain由Handler对象和Handler拦截器组成,由HandlerMapping的getHandler方法返回,RequestMappingHandlerMapping将adaptedInterceptors传递给HandlerExecutionChain的interceptorList
HandlerInterceptor接口允许自定义Handler执行链,并为Handler注册已存在或者自定义的拦截器
AbstractHandlerMapping是HandlerMapping的抽象类,支持优先级排序、默认的Handler和handler拦截器
HandlerMapping根据请求信息调用getHandler方法获取HandlerExecutionChain
DispatcherServlet的doDispatch方法处理HandlerExecutionChain,该类含有HandlerMethod和interceptorList,在applyPreHandle和applyPostHandle方法之间调用Handler,triggerAfterCompletion最后运行
5.参考
https://docs.spring.io/spring/docs/current/javadoc-api/
https://github.com/spring-projects/spring-framework
文中难免有不足,还望指出
SpringMVC源码阅读:拦截器的更多相关文章
- springMVC源码分析--拦截器HandlerExecutionChain(三)
上一篇博客springMVC源码分析--HandlerInterceptor拦截器调用过程(二)中我们介绍了HandlerInterceptor的执行调用地方,最终HandlerInterceptor ...
- SpringMVC源码阅读:核心分发器DispatcherServlet
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将介绍SpringMVC的核 ...
- SpringMVC源码阅读:视图解析器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:异常解析器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读:过滤器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- SpringMVC源码阅读系列汇总
1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...
- Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
- SpringMVC源码阅读:定位Controller
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码分析,弄清楚Spr ...
- SpringMVC源码阅读:Controller中参数解析
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
随机推荐
- hdu 4911 求逆序对数+树状数组
http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能 ...
- Redis中在程序中的应用
1.导入redis的配置文件,因为要交给web容器管理,所以直接命名为ApplicationContext-redis.xml,具体配置如下: <beans xmlns="http:/ ...
- ASP.NET实现头像剪切保存
利用swfupload上传头像,利用Jcrop来实现头像在线选择,然后提交个ashx对原头像进行剪切.代码如下: default.aspx: <%@ Page Language="C# ...
- 连接Oracle时ORA-12541 TNS 无监听程序
http://www.cnblogs.com/Qian123/archive/2016/08/24/5803085.html
- JS学习笔记9_JSON
1.JSON概述 JavaScript Object Natation,js对象表示法,(像XML一样)是一种数据格式,它与js有相同的语法形式 P.S.一点小历史:JSON之父是道格拉斯,<J ...
- 记录.NET Core在CentOS上基于Jenkins自动化发布
1.安装Jenkins,我这里采用的是非docker方式安装(两种都行,任选一种) 参考:https://www.cnblogs.com/xiaxiaolu/p/10357806.html https ...
- C#后台代码获取程序集资源文件
资源会被打包在程序集内部. 选择这种生成方式后,该资源文件会被嵌入到该应用的程序集中,就是说打开生成的应用程序目录是看不到这个文件的. 可以用相对于当前的XAML文件的相对Uri访问,<Imag ...
- 迁移桌面程序到MS Store(2)——Desktop App Converter
迁移传统桌面程序到MS Store的另一种方式是使用Desktop App Converter工具.虽然本篇标题包含了Desktop App Converter(以下简称DAC),实际上我是来劝你别用 ...
- SpringBoot入门之内嵌Tomcat配置
spring boot默认web程序启用tomcat内嵌容器tomcat,监听8080端口,servletPath默认为 / .需要用到的就是端口.上下文路径的修改,在spring boot中其修改方 ...
- C#6.0语言规范(十九) 文档注释
C#为程序员提供了一种机制,可以使用包含XML文本的特殊注释语法来记录他们的代码.在源代码文件中,具有特定形式的注释可用于指示工具从这些注释和它们之前的源代码元素生成XML.使用这种语法的注释称为文档 ...