首先,先简单的说一下怎么配置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拦截器运行原理。的更多相关文章

  1. SpringMVC 拦截器实现原理和登录实现

    SpringMVC 拦截器的原理图 springMVC拦截器的实现一般有两种方式 第一种方式是要定义的Interceptor类要实现了Spring的HandlerInterceptor 接口 第二种方 ...

  2. SpringMVC拦截器详解[附带源码分析]

    目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:h ...

  3. SpringMVC拦截器详解

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

  4. [转]SpringMVC拦截器详解[附带源码分析]

      目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:ht ...

  5. SpringMVC拦截器详解[附带源码分析](转)

    本文转自http://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html 感谢作者 目录 前言 重要接口及类介绍 源码分析 拦截器的配置 ...

  6. SpringMVC 拦截器原理

    前言 SpringMVC 拦截器也是Aop(面向切面)思想构建,但不是 Spring Aop 动态代理实现的, 主要采用责任链和适配器的设计模式来实现,直接嵌入到 SpringMVC 入口代码里面. ...

  7. 【SSM】拦截器的原理、实现

    一.背景: 走过了双11,我们又迎来了黑色星期五,刚过了黑五,双12又将到来.不管剁手的没有剁手的,估计这次都要剁手了!虽然作为程序猿的我,没有钱但是我们长眼睛了,我们关注到的是我们天猫.淘宝.支付宝 ...

  8. Java Servlet 过滤器与 springmvc 拦截器的区别?

    前言:在工作中,遇到需要记录日志的情况,不知道该选择过滤器还是拦截器,故总结了一下. servlet 过滤器 定义 java过滤器能够对目标资源的请求和响应进行截取.过滤器的工作方式分为四种 应用场景 ...

  9. 【ssm】拦截器的原理及实现

    一.背景: 走过了双11,我们又迎来了黑色星期五,刚过了黑五,双12又将到来.不管剁手的没有剁手的,估计这次都要剁手了!虽然作为程序猿的我,没有钱但是我们长眼睛了,我们关注到的是我们天猫.淘宝.支付宝 ...

随机推荐

  1. 64 位 win7 使用PLSQL Developer(转)

    由于 PLSQL Developer 没有64位版本,所以在64位系统上运行该程链接64位Oracle时就会报错,笔者为这个问题纠结了好几天,后来通过请教Google + 动手实践,终于搞定了这个问题 ...

  2. 关于ueditor与arcgis js api同用会报错的问题

    在ueditor与arcgis js api共用时,需要将ueditor\third-party\zeroclipboard\ZeroClipboard.js中的 if (typeof define ...

  3. Qt Read and Write Csv File

    This page discusses various available options for working with csv documents in your Qt application. ...

  4. shell编程9*9乘法表

    </pre>脚本内容<pre name="code" class="plain">#!/bin/bash for i in " ...

  5. mybatis使用原始Dao开发中存在的问题

    1.Dao方法存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的送数据库操作方法. 2.调用SqlSession的数据库需要制定statement ...

  6. ubuntu账户密码正确但是登录不进去系统

    ubuntu12.04管理员账户登录不了桌面,只能客人会话登录 求助!!ubuntu12.04管理员账户登录不了桌面,只能客人会话登录. ctrl+alt+f1 ,切换到tty1,输入管理员帐号和密码 ...

  7. code3289 花匠

    题目大意是求一个最长的抖动的子序列 题解中有一个大神写下了这样的代码: #include<cstdio> ,b=,x,y; int mmax(int a,int b) { if(a> ...

  8. oepnni安装

    sudo apt-get install libopenni-dev libopenni2-dev / apt-get install libqhull-dev

  9. ps 中添加一张图片

    // 测试打开一个文件var fileref = new File ("/E/work/没有图片提交/2014/2014.5.19/G20/部件渲染测试/png/tianji_1-41001 ...

  10. android 播放视频时切换全屏隐藏状态栏

    1. Demo: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstance ...