以下源码版本(4.2.0.RELEASE)

dispacherServlet是servlet的实现类,是spring MVC的前端转发器,是spring MVC的核心。

那么它做了哪些事呢?

它主要做了两件事:

NO1:

看如下源码:

/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
//初始化分段上传解析器(成对文件上传的解析和封装工作),没有默认的实现
initMultipartResolver(context);
//初始化地域解析器,默认实现是AcceptHeaderLocaleResolver
initLocaleResolver(context);
//初始化主题解析器,默认实现是FixedThemeResolver
initThemeResolver(context);
//初始化处理器映射,这是个集合, 默认实现是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping
initHandlerMappings(context);
//初始化处理器适配器,这是个集合,默认实现是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter
initHandlerAdapters(context);
//初始化处理器异常解析器,这是个集合,默认实现是AnnotationMethodHandlerExceptionResolver,ResponseStatusExceptionResolver和DefaultHandlerExceptionResolver
initHandlerExceptionResolvers(context);
//初始化请求到视图名解析器,默认实现是DefaultRequestToViewNameTranslator
initRequestToViewNameTranslator(context);
//初始化视图解析器,这是个集合,默认实现是InternalResourceViewResolver
initViewResolvers(context);
}

NO2:

每次请求都会调用它的doService方法,在doService方法中调用它的doDispatch方法。

doService方法可看代码中的两处注释:

重点在于调用它的doDispatch方法:

doDispatch(request, response);

首先我们来看方法doDispatch的注释:

可以简单理解成:所有的请求都将在这个方法里映射到指定的action的指定的方法(前提:请求路径正确)。

我们来一步一步解析这个方法:

以下,为

doDispatch方法的源码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// processedRequest是经过checkMultipart方法处理过的request请求
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()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
} if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} // Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(processedRequest, 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
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

第一行新建的变量processedRequest 会在

后面某一行(在doDispatch方法中,如下:)

processedRequest = checkMultipart(request);

中被方法处理加工,checkMultipart方法也是dispacherServlet类中的一个方法,源码如下:

那么这个方法的作用是什么呢?

关键是下面这一句:

this.multipartResolver != null && this.multipartResolver.isMultipart(request)

这一句代码做了如下两个判断:

1.dispatcherServlet中初始化后的multipartResolver是否为空(配置文件中是否配置,初始化dispatcherServlet时没有默认实例);

2.multipartResolver的方法isMultipart的作用是判断本次请求是否为文件上传。

如下为isMultipart的源码:

通过注释,可简单理解为:该方法通常以检查content-type是否为multipart/form-data的方式判断本次请求是否为文件上传,但实际功能需要看具体解析器对该方法的实现细节。

checkMultipart方法大致如此,再具体一些的内容在这里我就不赘述了,有兴趣的朋友可以自己看源码。

(多嘴一句:若想使用multipartResolver不要忘了增加jar包支持:commons.fileupload,common.io).

---------------------------------------

方法doDispatch的第二行新建了变量mappedHandler ,

类型为HandlerExecutionChain ,通过查看该类的源码,可清楚其作用,如下:

可理解其为一个处理链,包含了所有的handler和拦截器。

以下三行代码为doDispatch的核心:

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                // Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

通过注释,可理解前一句的作用:找到指定的handler,

后一句:找到对应的适配器adapter,

第三句:在这个函数里面会真正执行request请求相对于handler的方法(这只是大概的代码流程,真正的调用方法之前还有很多先前处理。)

以下是我debug断点的显示数据:

该方法时DispatcherServlet类中的方法getHandler,可以看到这里会对所有handlerMapper进行遍历,后执行HandlerMappering的getHandler方法,

如下:为HandlerMappering的getHandler方法:

这个方法会返回一个handler和所有的拦截器,组装成了一个HandlerExecutionChain类。也就是在方法doDispatch第二行代码里定义的mappedHandler,

该方法的具体实现是在org.springframework.web.servlet.handler.AbstractHandlerMapping中,如下:

    /**
* Look up a handler for the given request, falling back to the default
* handler if no specific one is found.
* @param request current HTTP request
* @return the corresponding handler instance, or the default handler
* @see #getHandlerInternal
*/
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
} HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}

这里的getHandlerInternal是个抽象方法,有具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,若为string类型,则表示要去spring配置容器中去找这个bean,

该方法剩下的部分正如HandlerMappering的getHandler方法注释所描述的:返回一个handler和所有的拦截器(有兴趣可深入了解)。

--------------------------

再来看doDispath方法里的

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

它首先调用了dispatcherServlet里的 getHandlerAdapter方法;看其源码如下:

看其注释:该方法会通过已确定映射的handler类来寻找满足条件的adapter,(注意,若没有找到,会直接报servletException异常)。

这个方法的核心是 方法supports,这是个抽象方法,不同的适配器会有不同的内部细节,如下分别为

SimpleControllerHandlerAdapter和SimpleServletHandlerAdapter的实现:


通过supports方法,dispatcherervlet类可以确定本次请求request映射的adapter.

------------------------

最后看

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

这里的mv是一个ModelAndView类,ha是前面获得的适配器adapter,

方法handler()是个抽象方法,具体实现细节依赖于适配器adapter的实现类,

但我门可看其注释,理解该方法的作用,
源码如下:

其注释的意思是是:使用提供的handler(前面确定的handler)来处理这个request请求。

以下,已实现类SimpleControllerHandlerAdapter为例,

这里会执行handlerRequest(该方法的实现细节依赖于不同的实现类,最后返回一个ModelAndAndView。

以上,为我本次研读dispatcherServlet类的所得。

可总结为,dispatcherServlet根据默认或配置的handlermapping和adapter,选择不同的方式对请求request请求进行处理,最后返回一个视图ModeAndView。

(那么返回值为json是如何实现的?····问题越来越多了)

----------------------------------补充----------------------------

可以看出,关于

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

部分,阐述的比较模糊,或者说,基本没什么描述,虽然这部分是核心,

我试图过阐述其实现细节,但这部分的实现细节根据不同的handlerMapping和不同的adpater而不同,

所以,我准备在其他章节对其进行分别阐述。

spring MVC mybatis dispacherServlet(源码解读)的更多相关文章

  1. 0002 - Spring MVC 拦截器源码简析:拦截器加载与执行

    1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日 ...

  2. spring MVC 原理及源码解析

    首先要知道springmvc的核心控制器dispatcherServlet(继承自httpServlet) 一般httpServlet的请求过程: 1.初始化(创建servlet实例时)时会执行ser ...

  3. ssm整合说明与模板-Spring Spring MVC Mybatis整合开发

    ssm整合说明 spring+spring mvc+mybatis 说明 源码下载 由于之前存在ssh框架,spring+struts+hibernate,其中spring负责aop与ioc,所以一般 ...

  4. spring IOC DI AOP MVC 事务, mybatis 源码解读

    demo https://gitee.com/easybao/aop.git spring DI运行时序 AbstractApplicationContext类的 refresh()方法 1: pre ...

  5. Spring3 + Spring MVC+ Mybatis 3+Mysql 项目整合(注解及源码)

    Spring3 + Spring MVC+ Mybatis 3+Mysql 项目整合(注解及源码) 备注: 之前在Spring3 + Spring MVC+ Mybatis 3+Mysql 项目整合中 ...

  6. MyBatis源码解读(1)——SqlSessionFactory

    在前面对MyBatis稍微有点了解过后,现在来对MyBatis的源码试着解读一下,并不是解析,暂时定为解读.所有对MyBatis解读均是基于MyBatis-3.4.1,官网中文文档:http://ww ...

  7. MyBatis源码解读之延迟加载

    1. 目的 本文主要解读MyBatis 延迟加载实现原理 2. 延迟加载如何使用 Setting 参数配置 设置参数 描述 有效值 默认值 lazyLoadingEnabled 延迟加载的全局开关.当 ...

  8. Mybatis源码解读-SpringBoot中配置加载和Mapper的生成

    本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载. 建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此 ...

  9. MyBatis源码解读(3)——MapperMethod

    在前面两篇的MyBatis源码解读中,我们一路跟踪到了MapperProxy,知道了尽管是使用了动态代理技术使得我们能直接使用接口方法.为巩固加深动态代理,我们不妨再来回忆一遍何为动态代理. 我相信在 ...

随机推荐

  1. 查找素数Eratosthenes筛法的mpi程序

    思路: 只保留奇数 (1)由输入的整数n确定存储奇数(不包括1)的数组大小: n=(n%2==0)?(n/2-1):((n-1)/2);//n为存储奇数的数组大小,不包括基数1 (2)由数组大小n.进 ...

  2. sql select 1-10的数字

    SELECT V FROM (   VALUES (1), (2), (3), (4), (5),          (6), (7), (8), (9), (10) ) [1 to 10](V)

  3. Nginx模块之http.md

    ngx_http_access_module ngx_http_access_module模块允许限制对某些客户端地址的访问. 访问也可以通过密码,子请求的结果或JWT来限制. 通过地址和密码的同时访 ...

  4. NHibernate常见问题及解决方法

    NHibernate常见问题及解决方法 曾经学过NHibernate的,但是自从工作到现在快一年了却从未用到过,近来要巩固一下却发现忘记了许多,一个"in expected: <end ...

  5. js封装的三级联动菜单(使用时只需要一行js代码)

    前言 在实际的项目开发中,我们经常需要三级联动,比如省市区的选择,商品的三级分类的选择等等. 而网上却找不到一个代码完整.功能强大.使用简单的三级联动菜单,大都只是简单的讲了一下实现思路. 下面就给大 ...

  6. EncodingHelper

    /// <summary> /// Url解码 /// </summary> /// <param name="str">原始字符串</p ...

  7. 关于我们经常用到的form表单提交

    工作中遇到了太多太多的表单提交问题,曾经学过一个HTML的表单提交给 另外一个HTML页面,对于后台怎么获取有点想不起来了. 今天便做了几个实验,提交订单到后台,来掩饰后台如何接受表单内容: 实验 一 ...

  8. 预览github上的html页面

    譬如有个项目:https://github.com/wozhizui/ife/tree/DevTogether/task19 里面有html的示例文件index.html 我们点击进去看到的是一堆代码 ...

  9. php stdclass转数组

    打印输出是这样 object(stdClass)[11] //object public 'xx' => string 'xxxxxx' (length=21)可用函数处理 get_object ...

  10. 我用ANDROID STUDIO开发,页面上总包这个警告,很烦!网上说是sdk版本问题,但是我是基于25开发的,最小版本也是19,有没有老司机啊?3克油