SpringMvc拦截器运行原理。
首先,先简单的说一下怎么配置SpringMvc的拦截器。
分两步,第一步先定义一个类,实现HandlerInterceptor接口。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; public class TestInterceptor implements HandlerInterceptor{ @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("preHandle invoke");
return true;
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle invoke");
} @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion invoke"); } }
第二布,配置springMvc.xml
<!-- 拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean class="com.mmc.interceptor.TestInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
完工。下面分析原理
打开这个DispatcherServlet类,这个类是SpringMvc中最核心的一个类。答案就在doDispatch方法里面:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
里面我标红了四处,第一处对应执行自定义拦截器的preHandler方法,第二处对应执行你的Controller类中的RequestMapping映射的处理方法,第三处是自定义拦截器的postHandle方法,第四处是自定义拦截器的afterCompletion方法。
这些可能很多人都知道,但是我们肯定还有疑问,为什么他能调用到我的拦截器里写的方法呢?
接着往下分析。
于是我们点开我第一处标红那里的mappedHandler.applyPreHandle方法。代码如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (getInterceptors() != null) {
for (int i = 0; i < getInterceptors().length; i++) {
HandlerInterceptor interceptor = getInterceptors()[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
看得出来,他是在遍历getInterceptors(),所以在点开这个方法。
public HandlerInterceptor[] getInterceptors() {
if (this.interceptors == null && this.interceptorList != null) {
this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
}
return this.interceptors;
}
这里面有两个变量,interceptorList和interceptors。这时我肯定会猜想,肯定是某个时候系统框架把这两个变量赋值了,然后在我执行方法的时候都会去看看这两个变量是不是有值的。有就执行拦截器。
那么这两个变量是什么时候赋值的呢?找到定义这两个变量的地方,他们是在HandlerExecutionChain类里面,我把断点打在这个变量定义那里。debug运行,代码停在了HandlerExecutionChain里面
看debug模式中的方法执行顺序,点到doDispatch方法里面,在执行applyPreHandle即调用拦截器的方法之前,有一个getHandler方法,这个方法里面有getHandlerExecutionChain方法。这个方法里面有如下代码:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain =
(handler instanceof HandlerExecutionChain) ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
chain.addInterceptors(getAdaptedInterceptors());
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
注意看,里面有一句chain.addInterceptor(mappedInterceptor.getInterceptor());这个从名字就知道是添加拦截器。而这个拦截器是从mappedInterceptor获得的属性,mappedInterceptor是遍历mappedInterceptors得到的。而这个对象在我运行这个的时候就已经存在了。所以我思考是不是启动项目的时候就已经初始化了,而且是根据我的xml配置文件来初始化的。于是我在mappedInterceptors对象处打断点,启动tomcat。果然代码执行到了。在AbstractHandlerMapping类中找到了这个方法,
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.mappedInterceptors);
initInterceptors();
在这个detectMappedInterceptors(this.mappedInterceptors);里面便是将mappedInterceptors对象赋值了。下面我就不贴代码了。就是去找MappedInterceptor类,这个类是根据xml文件中<mvc:interceptor>的内容来构建的。构建方法在InterceptorsBeanDefinitionParser类里面。
顺着来一遍就是这样的:
启动项目,InterceptorsBeanDefinitionParser类会根据配置文件构建MappedInterceptor对象集合,当我发送请求的时候,系统去看这个集合里有没有值,如果有值,就遍历执行这个对象,取得这个对象中的HandlerInterceptor属性,也就是我自定义的拦截器的对象。然后去执行我这个拦截器中的三个方法。到此拦截器的运行原理就分析完了。
有分析不对的地方请不吝指出
SpringMvc拦截器运行原理。的更多相关文章
- SpringMVC 拦截器实现原理和登录实现
SpringMVC 拦截器的原理图 springMVC拦截器的实现一般有两种方式 第一种方式是要定义的Interceptor类要实现了Spring的HandlerInterceptor 接口 第二种方 ...
- SpringMVC拦截器详解[附带源码分析]
目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:h ...
- SpringMVC拦截器详解
拦截器是每个Web框架必备的功能,也是个老生常谈的主题了. 本文将分析SpringMVC的拦截器功能是如何设计的,让读者了解该功能设计的原理. 重要接口及类介绍 1. HandlerExecution ...
- [转]SpringMVC拦截器详解[附带源码分析]
目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:ht ...
- SpringMVC拦截器详解[附带源码分析](转)
本文转自http://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html 感谢作者 目录 前言 重要接口及类介绍 源码分析 拦截器的配置 ...
- SpringMVC 拦截器原理
前言 SpringMVC 拦截器也是Aop(面向切面)思想构建,但不是 Spring Aop 动态代理实现的, 主要采用责任链和适配器的设计模式来实现,直接嵌入到 SpringMVC 入口代码里面. ...
- 【SSM】拦截器的原理、实现
一.背景: 走过了双11,我们又迎来了黑色星期五,刚过了黑五,双12又将到来.不管剁手的没有剁手的,估计这次都要剁手了!虽然作为程序猿的我,没有钱但是我们长眼睛了,我们关注到的是我们天猫.淘宝.支付宝 ...
- Java Servlet 过滤器与 springmvc 拦截器的区别?
前言:在工作中,遇到需要记录日志的情况,不知道该选择过滤器还是拦截器,故总结了一下. servlet 过滤器 定义 java过滤器能够对目标资源的请求和响应进行截取.过滤器的工作方式分为四种 应用场景 ...
- 【ssm】拦截器的原理及实现
一.背景: 走过了双11,我们又迎来了黑色星期五,刚过了黑五,双12又将到来.不管剁手的没有剁手的,估计这次都要剁手了!虽然作为程序猿的我,没有钱但是我们长眼睛了,我们关注到的是我们天猫.淘宝.支付宝 ...
随机推荐
- Mac下Laravel的Homestead环境配置
准备开始学PHP后端,在看完PHP语法以及PHP的MVC设计模式以后,准备开始学习PHP的主流框架Laravel,Laravel准备了一个万事俱备的虚拟环境Homestead,配置一下用起来. 安装v ...
- loadrunner - Run time Settings 的详细说明
本文主要讲解一下run-time settings(如图1所示)里各设置项的具体含义(注:标红色的选项卡是比较值得关注的,可重点看一下): 图1 1.General / Run Logic 选项卡 ...
- Linux 基础教程 26-基础网络配置
基本配置 要想上网,计算机需要有专门的网络连接设备,即网络接口卡或者网卡.网卡按照与计算机主机的连接方式可以分为PCI网卡.ISA网卡及无线网卡(USB网卡)等.在Linux中可以使用命令ls ...
- Codeforces766B Mahmoud and a Triangle 2017-02-21 13:47 113人阅读 评论(0) 收藏
B. Mahmoud and a Triangle time limit per test 2 seconds memory limit per test 256 megabytes input st ...
- DW2.0
一.DW2.0从企业的角度,吸引企业的原因: 1.数据仓库基础设施的成本不再持续增长.在第一代数据仓库中,技术基础设施的成本是不断增长的,随着数据量的增长,基础设施的成本会以指数级增长.但是使用DW2 ...
- 企业搜索引擎开发之连接器connector(二十)
连接器里面衔接数据源与数据推送对象的是QueryTraverser类对象,该类实现了Traverser接口 /** * Interface presented by a Traverser. Used ...
- 试题 G: 外卖店优先级 第十届蓝桥杯
试题 G: 外卖店优先级时间限制: 1.0s 内存限制: 512.0MB 本题总分: 20 分[问题描述]“饱了么”外卖系统中维护着 N 家外卖店,编号 1 ∼ N.每家外卖店都有一个优先级,初始时 ...
- MySQL—练习2
参考链接:https://www.cnblogs.com/edisonchou/p/3878135.html 感谢博主 https://blog.csdn.net/flycat296/articl ...
- Android-自定义开关(升级版)
效果图: 定义一个类,取名为MySwitch.java,此类去继承View,为何是继承View而不是去继承ViewGroup呢,是因为自定义开关没有子控件,之需要操作自身绘制即可 package cu ...
- sitecore 缓存管理器
namespace XXX.Shared.Infrastructure.Caching { using System; using System.Collections.Generic; using ...