文档章节

SpringMVC执行流程及源码解析

加大装益达
 加大装益达
发布于 2017/04/06 22:55
字数 2711
阅读 5132
收藏 438
点赞 28
评论 9

原文链接

谢谢各位点赞评论收藏~自己学习时候记录的东西,没想到能被关注~自己理解还不太好,希望各位看的时候不要全部按照文章里写的来,要自己思考一下,如有不对还请指出,只希望不会误导别人。

在SpringMVC中主要是围绕着DispatcherServlet来设计,可以把它当做指挥中心。这里先说明一下SpringMVC文档给出的执行流程,然后是我们稍微具体的执行流程,最后是流程大致的源码跟踪。关于很很很详细的源码解析,这里暂先不做。

官方文档中的流程

首先看下SpringMVC文档上给的流程图:

DispatcherServlet.png

这张图片给了我们大概的执行流程:

  1. 用户请求首先发送到前端控制器DispatcherServlet,DispatcherServlet根据请求的信息来决定使用哪个页面控制器Controller(也就是我们通常编写的Controller)来处理该请求。找到控制器之后,DispatcherServlet将请求委托给控制器去处理。
  2. 接下来页面控制器开始处理用户请求,页面控制器会根据请求信息进行处理,调用业务层等等,处理完成之后,会把结果封装成一个ModelAndView返回给DispatcherServlet。
  3. 前端控制器DispatcherServlet接到页面控制器的返回结果后,根据返回的视图名选择相应的试图模板,并根据返回的数据进行渲染。
  4. 最后前端控制器DispatcherServlet将结果返回给用户。

更具体的流程

上面只是总体流程,接下来我们稍微深入一点,看下更具体的流程,这里没有图,只有步骤解析:

  1. 用户请求发送到前端控制器DispatcherServlet。
  2. 前端控制器DispatcherServlet接收到请求后,DispatcherServlet会使用HandlerMapping来处理,HandlerMapping会查找到具体进行处理请求的Handler对象。
  3. HandlerMapping找到对应的Handler之后,并不是返回一个Handler原始对象,而是一个Handler执行链,在这个执行链中包括了拦截器和处理请求的Handler。HandlerMapping返回一个执行链给DispatcherServlet。
  4. DispatcherServlet接收到执行链之后,会调用Handler适配器去执行Handler。
  5. Handler适配器执行完成Handler(也就是我们写的Controller)之后会得到一个ModelAndView,并返回给DispatcherServlet。
  6. DispatcherServlet接收到Handler适配器返回的ModelAndView之后,会根据其中的视图名调用视图解析器。
  7. 视图解析器根据逻辑视图名解析成一个真正的View视图,并返回给DispatcherServlet。
  8. DispatcherServlet接收到视图之后,会根据上面的ModelAndView中的model来进行视图中数据的填充,也就是所谓的视图渲染。
  9. 渲染完成之后,DispatcherServlet就可以将结果返回给用户了。

源码

DispatcherServlet是一个Servlet,我们知道在Servlet在处理一个请求的时候会交给service方法进行处理,这里也不例外,DispatcherServlet继承了FrameworkServlet,首先进入FrameworkServlet的service方法:

protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
	//请求方法
    String method = request.getMethod();
    //PATCH方法单独处理
    if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
        processRequest(request, response);
    }
    else {//其他的请求类型的方法经由父类,也就是HttpServlet处理
        super.service(request, response);
    }
}

HttpServlet中会根据请求类型的不同分别调用doGet或者doPost等方法,FrameworkServlet中已经重写了这些方法,在这些方法中会调用processRequest进行处理,在processRequest中会调用doService方法,这个doService方法就是在DispatcherServlet中实现的。下面就看下DispatcherServlet中的doService方法的实现。

请求到达DispatcherServlet

doService方法:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

    //给request中的属性做一份快照
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        logger.debug("Taking snapshot of request attributes before include");
        attributesSnapshot = new HashMap<String, Object>();
        Enumeration<?> attrNames = request.getAttributeNames();
        while (attrNames.hasMoreElements()) {
            String attrName = (String) attrNames.nextElement();
            if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
    }

    //如果我们没有配置类似本地化或者主题的处理器之类的
    //SpringMVC会使用默认的值
    //默认配置文件是DispatcherServlet.properties
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

    FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
    if (inputFlashMap != null) {
        request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
    }
    request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
    request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

    try {
    	//开始处理
        doDispatch(request, response);
    }
    finally {
        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            return;
        }
        // Restore the original attribute snapshot, in case of an include.
        if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
        }
    }
}

DispatcherServlet开始真正的处理,doDispatch方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
	//SpringMVC中异步请求的相关知识,暂先不解释
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
        	//先检查是不是Multipart类型的,比如上传等
            //如果是Multipart类型的,则转换为MultipartHttpServletRequest类型
            processedRequest = checkMultipart(request);
            multipartRequestParsed = processedRequest != request;

            //获取当前请求的Handler
            mappedHandler = getHandler(processedRequest, false);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            //获取当前请求的Handler适配器
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // 对于header中last-modified的处理
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }
			//拦截器的preHandle方法进行处理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            try {
                //真正调用Handler的地方
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
            }
			//处理成默认视图名,就是添加前缀和后缀等
            applyDefaultViewName(request, mv);
            //拦截器postHandle方法进行处理
            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);
        }
    }
}

可以看到大概的步骤还是按照我们上面分析的走的。

查找请求对应的Handler对象

对应着这句代码mappedHandler = getHandler(processedRequest, false);,看下具体的getHandler方法:

protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
    return getHandler(request);
}

继续往下看getHandler:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//遍历所有的handlerMappings进行处理
    //handlerMappings是在启动的时候预先注册好的
    for (HandlerMapping hm : this.handlerMappings) {
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

继续往下看getHandler,在AbstractHandlerMapping类中:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	//根据request获取handler
    Object handler = getHandlerInternal(request);
    if (handler == null) {
    	//如果没有找到就使用默认的handler
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    //如果Handler是String,表明是一个bean名称
    //需要超照对应bean
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }
    //封装Handler执行链
    return getHandlerExecutionChain(handler, request);
}

根据requrst获取handler

首先看下根据requrst获取handler步骤getHandlerInternal方法,在AbstractHandlerMethodMapping中:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    //获取request中的url,用来匹配handler
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    //根据路径寻找Handler
    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    //根据handlerMethod中的bean来实例化Handler并添加进HandlerMethod
    return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
}

看下根据路径寻找handler的方法lookupHandlerMethod:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<Match>();
	//直接匹配
    List<T> directPathMatches = this.urlMap.get(lookupPath);
    //如果有匹配的,就添加进匹配列表中
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
	//还没有匹配的,就遍历所有的处理方法查找
    if (matches.isEmpty()) {
        // No choice but to go through all mappings
        addMatchingMappings(this.handlerMethods.keySet(), matches, request);
    }
	//找到了匹配的
    if (!matches.isEmpty()) {
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        Collections.sort(matches, comparator);
		//排序之后,获取第一个
        Match bestMatch = matches.get(0);
        //如果有多个匹配的,会找到第二个最合适的进行比较一下
        if (matches.size() > 1) {
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                throw new IllegalStateException(
                        "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
                        m1 + ", " + m2 + "}");
            }
        }
		//设置request参数
        handleMatch(bestMatch.mapping, lookupPath, request);
        //返回匹配的url的处理的方法
        return bestMatch.handlerMethod;
    }
    else {//最后还没有找到,返回null
        return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
    }
}

获取默认Handler

如果上面没有获取到Handler,就会获取默认的Handler。如果还获取不到就返回null。

处理String类型的Handler

如果上面处理完的Handler是String类型的,就会根据这个handlerName获取bean。

封装Handler执行链

上面获取完Handler,就开始封装执行链了,就是将我们配置的拦截器加入到执行链中去,getHandlerExecutionChain:

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
	//如果当前Handler不是执行链类型,就使用一个新的执行链实例封装起来
    HandlerExecutionChain chain =
        (handler instanceof HandlerExecutionChain) ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
	//先获取适配类型的拦截器添加进去拦截器链
    chain.addInterceptors(getAdaptedInterceptors());
	//当前的url
    String lookupPath = urlPathHelper.getLookupPathForRequest(request);
    //遍历拦截器,找到跟当前url对应的,添加进执行链中去
    for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
        if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
        }
    }

    return chain;
}

获取对应请求的Handler适配器

getHandlerAdapter:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	//遍历所有的HandlerAdapter,找到和当前Handler匹配的就返回
    //我们这里会匹配到RequestMappingHandlerAdapter
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (ha.supports(handler)) {
            return ha;
        }
    }
}

缓存的处理

也就是对last-modified的处理

执行拦截器的preHandle方法

就是遍历所有的我们定义的interceptor,执行preHandle方法

使用Handler适配器执行当前的Handler

ha.handle执行当前Handler,我们这里使用的是RequestMappingHandlerAdapter,首先会进入AbstractHandlerMethodAdapter的handle方法:

public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    return handleInternal(request, response, (HandlerMethod) handler);
}

handleInternal方法,在RequestMappingHandlerAdapter中:

protected final ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
        // Always prevent caching in case of session attribute management.
        checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
    }
    else {
        // Uses configured default cacheSeconds setting.
        checkAndPrepare(request, response, true);
    }

    // Execute invokeHandlerMethod in synchronized block if required.
    if (this.synchronizeOnSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                return invokeHandleMethod(request, response, handlerMethod);
            }
        }
    }
	//执行方法,封装ModelAndView
    return invokeHandleMethod(request, response, handlerMethod);
}

组装默认视图名称

前缀和后缀名都加上

执行拦截器的postHandle方法

遍历intercepter的postHandle方法。

处理最后的结果,渲染之类的

processDispatchResult方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
        HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

    boolean errorView = false;

    if (exception != null) {
        if (exception instanceof ModelAndViewDefiningException) {
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        }
        else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
    	//渲染
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
    else {
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

重点看下render方法,进行渲染:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    //设置本地化
    Locale locale = this.localeResolver.resolveLocale(request);
    response.setLocale(locale);

    View view;
    if (mv.isReference()) {
        //解析视图名,得到视图
        view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
    }
    else {
        // No need to lookup: the ModelAndView object contains the actual View object.
        view = mv.getView();
        if (view == null) {
            throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                    "View object in servlet with name '" + getServletName() + "'");
        }
    }

    //委托给视图进行渲染
    view.render(mv.getModelInternal(), request, response);
}

view.render就是进行视图的渲染,然后跳转页面等处理。

到这里大概的流程就走完了。其中涉及到的东西还有很多,暂先不做详细处理。

© 著作权归作者所有

共有 人打赏支持
加大装益达
粉丝 30
博文 80
码字总数 132506
作品 0
浦东
高级程序员
加载中

评论(9)

l
lsh2015
感谢分享!
LiJinYang
LiJinYang
多谢分享,分析的很到位
卷了个蛋卷
卷了个蛋卷
6666666
悲喜世界
悲喜世界
点赞
猫咪要感冒
猫咪要感冒
我的名字叫李猜
我的名字叫李猜
666
卓sir
卓sir
相当不错
骑着上帝去浪
骑着上帝去浪
手动点赞
Sheamus
Sheamus
分析的很好赞
Spring源码分析之Bean的解析

作者: 一字马胡 转载标志 【2017-12-28】 更新日志 前言 Spring源码分析是一个系列,源码是Spring 4.X,本系列主要分析Spring的代码执行流程,过于细节的内容将不会涉及太多,主要是为了理清...

疼丸李白
2017/12/28
0
0
Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的。我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反射创建一个原始的 ...

coolblog.xyz
06/11
0
0
Spring源码分析之WebMVC

作者: 一字马胡 转载标志 【2018-01-07】 更新日志 导入 Spring源码分析系列文章索引: Spring源码分析之Bean的解析 Spring源码分析之Bean的加载 Spring源码分析之AOP 本文是系列文章的第四...

疼丸李白
01/07
0
0
深入理解Spring源码(一)-IOC容器的定位,载入,注册

前言:Spring源码继承,嵌套层次非常多,读起来非常容易晕,小伙伴们在看文章的时候一定要跟着文章的思路自己去源码里点一点,看一看,并且多看几次。就会越来越清晰。下面开始正题 1.Spring...

Meet相识_bfa5
05/01
0
0
Spring IOC 容器源码分析 - 创建原始 bean 对象

1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续。在上一篇文章中,我们从战略层面上领略了方法的全过程。本篇文章,我们就从战术的层面上,详细分析方法中的一个重要的调用,即...

coolblog.xyz
06/06
0
0
spring源码解析上下文初始化ContextLoaderListener

前言 本文转自“天河聊技术”微信公众号 从本篇文章开始主要介绍spring源码解析相关的spring上下文初始化、bean定义解析、beanFactory创建、初始化、bean定义注册到beanFactory、bean实例化、...

天河2018
05/10
0
0
Dubbo源码-Dubbo是如何随心所欲自定义XML标签的

叨叨 今天考虑了很久要不要写这篇文章。 距离《Dubbo源码》系列的开篇到现在已经快两个月时间了。当时是想着工作上的RPC框架使用存在一些让人头疼的问题,就来看看Dubbo给出了一套什么样的解...

Jackie_Zheng
05/20
0
0
Spring AOP 源码分析 - 筛选合适的通知器

1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析。本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出合适的通知器(Advisor...

java高级架构牛人
06/21
0
0
Spring IOC 实现原理

Spring IOC 实现原理 IOC: Inversion of Control ,即 "控制反转" , 不是什么技术,而是一种思想。原先需要自行实例化的对象, 交给IOC容器去实现。那么控制反转,谁被控制? 谁被反转 ? 在...

起个名忒难
05/17
0
0
Spring MVC 原理探秘 - 一个请求的旅行过程

1.简介 在前面的文章中,我较为详细的分析了 Spring IOC 和 AOP 部分的源码,并写成了文章。为了让我的 Spring 源码分析系列文章更为丰富一些,所以从本篇文章开始,我将来向大家介绍一下 Sp...

coolblog.xyz
07/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

打印斐波那契数

package com.jerry.ch04;public class PrintFibonacci {public static void main(String[] args) {for (int i=0; i<10; i++) {System.out.print(fib(i) + " ");}......

JerryNing
4分钟前
0
0
shell编程

一、shell脚本介绍

人在艹木中
4分钟前
0
0
istio 0.8 遥测 案例

==============遥测===================================== 演示如何从网格中收集遥测信息。 分布式跟踪。如何配置代理以向Zipkin或Jaeger发送跟踪请求 收集度量标准和日志。此任务说明如何配...

xiaomin0322
6分钟前
0
0
ND4J求多元线性回归以及GPU和CPU计算性能对比

上一篇博客《梯度下降法求多元线性回归及Java实现》简单了介绍了梯度下降法,并用Java实现了一个梯度下降法求回归的例子。本篇博客,尝试用dl4j的张量运算库nd4j来实现梯度下降法求多元线性回...

冷血狂魔
7分钟前
0
0
springboot常用注解

@SpringBootApplication: 包含@Configuration、@EnableAutoConfiguration、@ComponentScan 通常用在主类上。 @Service: 用于标注业务层组件。 @RestController: 用于标注控制层组件(如strut...

GoldenVein
13分钟前
1
0
如何进行大数据的入门级学习?

不知道你是计算机专业应届生还是已经从业者。总之,有java基础的学生学习大数据会轻松很多,零基础的小白都需要从java和linux学起。 如果你是一个学习能力特别强,而且自律性也很强的人的话可...

董黎明
27分钟前
0
0
使用Parcelable传递复杂参数

最近做AIDL传递对象,对象必须实现Parcelable的方法才可以被传递。 @Override    public int describeContents() {//这个 默认返回0就行了。        return 0;    }    ...

火云
28分钟前
0
0
十大Intellij IDEA快捷键

Intellij IDEA中有很多快捷键让人爱不释手,stackoverflow上也有一些有趣的讨论。每个人都有自己的最爱,想排出个理想的榜单还真是困难。以前也整理过Intellij的快捷键,这次就按照我日常开发...

HJCui
38分钟前
0
0
word 使用mathtype 编写 数学公式

下载安装,这个链接命名。。。。 http://www.mathtype.cn/xiazai.html 安装之后会多出一个选项 使用内联方式插入图表 编写公式的界面 设置支持latex 语法 输入公式回车就可以看到结果...

阿豪boy
57分钟前
0
0
Promise

定义 Promise是异步编程的一种解决方案,所谓Promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个一步操作)的结果。 特点: 2.1 对象的状态不受外界影响,三种状态pending...

litCabbage
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部