SpringMVC执行流程

SpringMVC概述

Spring MVC属于SpringFrameWork的后续产物,已经融合在Spring Web Flow内里。Spring 框架提供了构建 Web 应用程序的全功效 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用Spring举行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架。

SpringMVC执行流程归纳综合

SpringMVC框架虽然壮大,然则其执行流程更是妙不可言。以是我们这次要用一个简朴的例子去深究一下SpringMVC的底层执行流程!

如下是SpringMVC的执行流程梗概图,我会在后面的底层流程剖析中重点提到梗概图中的这几个零件,以及它们的作用!


SpringMVC执行流程梗概图(切记:该图只是梳理思绪,并不稀奇严谨,请谅解)


springMVC执行流程

SpringMVC的主要组件(可视化组件)

既然,我们要选择剖析SpringMVC底层执行流程,那一定是要先剖析我们能所看到外面的MVC主要组件。这样我们剖析完可视组件后,就能找到剖析SpringMVC底层执行流程的入口,以是剖析它的主要组件显得更是主要!

SpringMVC的主要组件是由焦点的前端控制器(web.xml)、后端控制器(Controller)和spring-mvc.xml设置文件组成。

  • 焦点的前端控制器: 作为MVC框架,首先要解决的就是如何能收到请求。以是MVC框架大都市设计一款前端控制器(入口或者提及点),选型在Servlet或Filter两者之一,由前端控制器来最率先的事情,吸收请求。在SpringMVC中,也不破例,前端控制器的选型确定为Servlet(DispatcherServlet),此前端控制器在吸收请求后,还会卖力SpringMVC的焦点调剂治理,以是既是前端又是焦点。

  • 后端控制器: 后端控制器为Controller,等价于之前界说的Servlet。MVC框架中,后端控制器也是必不可少的主要组件之一。由于它吸收了用户请求的大量数据参数工具(或Json)存储在域中利便页面(JSP)取值,或是携带着这些数据返回所需要跳转(重定向或请求转发)的页面。这里值得注意的是,后端控制器本质并不是一个通俗的Servlet,也不是BaseServlet,它只是一个通俗的类,内里却像曾经的BaseServlet一样可以拥有很多个方式,这些方式在SpringMVC中成为一个个Handler(换汤不换药,本质仍然)。以是在MVC模式的执行流程环节中,后端控制器控制着页面的跳转和数据的通报,在这里也有着很高的职位。

  • spring-mvc.xml设置文件: 该设置文件设置着许多在执行历程中需要加载的组件,好比:注解扫描器、注解扫描驱动、试图剖析器、静态资源处置器、异常剖析器、拦截器、上传剖析器等等,若是我们要使用这些组件,就需要在该设置文件中注入这些组件的相关设置,注入设置后由SpringMVC工厂在执行历程中加载这些组件,以杀青我们使用这些组件的目的。以是这也是它受人青睐的缘故原由。

SpringMVC执行流程剖析

上述得知,我们执行流程剖析的入口既是焦点的前端控制器,即web.xml,那我们有资格领会该前端控制器中设置了什么!如下:

前端控制器


由上图所知,前端控制器中所包罗的即是同时启动SpringMVC工厂和Spring工厂,让两个工厂同时运作处置请求,并作出响应。既然要剖析SpringMVC的底层执行流程,那我们要从加载SpringMVC工厂的DispatcherServlet提及。首先进入到DispatcherServlet中,查看源代码所有方式,如下图所示:

DispatcherServlet源码所有方式


DispatcherServlet继续FrameworkServlet


上图所示,我进入到了DispatcherServlet中。既然说它是一个Servlet,那一定是需要寻找它的service方式,由于Service方式是Servlet的焦点所在。于是我打开了IDEA的方式列表搜索service方式,未果。虽然未果,然则我发现两个主要的线索,一是该Servlet中有一个doSerivce方式,二是DispatcherServlet继续了FrameworkServlet,我想既然子类没有service方式,父类一定有,于是我进入到了FrameworkServlet查看源代码,如下图所示:

FrameworkServlet源码


我兴冲冲在父类(FrameworkServlet)中找到了service方式,然则照样感受喜悦的太早了,该service方式中除了resolve方式获取请求方式和proce***equest方式外,我一无所知。随后竟然发现了红色箭头所指向的器械super.service(request, response);,这意味着什么呢?这意味着它继续了父类拥有的service方式,于是我点击super句点后面的service方式查看源码惊人的发现这个类竟然是HttpServlet,显然我们找service方式的这条路走到终点了。在内里有两个方式存在一个是resolve方式,它是获取请求方式的。另有一个方式不知道是做什么的,于是我点击了进去查看源码,如下图所示:

proce***equest方式源码


既然我们进去看到了proce***equest方式的源码,就要找主要的方式。作甚主要的方式呢,一样平常被try块包裹的方式一定是主要方式,于是我找到了doService(request, response);方式,并继续点击去看该doService方式的源码,如下图所示:

doService(request, response);方式源码


逐渐失去耐心的我真的被惊讶到了,进入到doService方式后,也没有跳到其他的类中,而且照样在该类中跳到了一个空的doService();方式中。唉,探讨事实真的是件不容易的事情呀~我叹了一口气。冷静下来一想,父类是空方式没有实现,那焦点逻辑代码必定是在子类中了呀。这不是多态嘛!于是,我得出了结论,费劲吧难,找入口的逻辑代码回过头来照样得看DispatcherServlet中的谁人doService方式。此时我知道,这必将是一个漫长的探索之路。于是,我秉着探讨原理的心态,再一次点进了被我错过的谁人DispatcherServlet中的doSerivce方式,如下图:

DispatcherServlet中的doService()方式


既然确定了这是探讨底层原理的最先,那我们就在doServie()方式中寻找主要的逻辑,于是我再一次的在try块中找到了一个名为doDispatch(request, response);的方式(省略了前面的种种初始化和存储域数据)。在探讨底层原理的道路上,你会发现越来越靠近真理,虽然这注定是一个漫长的探索历程,我也情愿。于是,点击进入到了doDispatch()方式中的源码,如下图所示:

doDispatch()方式源码


走进了doDispatch()方式的源码,才知道我没有看错你。内里标有注释的都是一些主要的执行逻辑方式。接下来我们会一个个的剖析,逐步深入明白SpringMVC的执行流程。既然探索执行流程那就少不了Debug(Debug调试功效,Debug能很清晰的看到执行流程),于是我在getHandler()方式的那一行打了一个断点。下一步跟进执行流程进入到了getHandler()方式,如下图所示:

getHandler方式源码(注释注释:为当前请求寻找并返回一个handler工具)


断点停留到了这一行,由于getHandler()的名字,顾名思义就是获取Controller层中的Handler。它是怎么获取到的呢?我们在断点的变量显示框中,看到handlerMappings是一个数组,其中有三个工具。他们可以划分以差别的方式处置差别的Handler,其中我们可以点击这个三个工具,一一把其工具睁开查看主要属性,如下图所示:

0 = {RequestMappingHandlerMapping}


2 = {SimpleUrlHandlerMapping}


如上图得知,RequestMappingHandlerMapping工具识别了我们Controller中的@RequestMapping注解和各个Handler上方的注解路径。SimpleUrlHandlerMapping工具识别了处置静态资源驱动所建立的谁人默认Servlet,而处置静态资源的默认Servlet路径给了/**,它识别了这个路径。HanderMapping映射器中的工具,通过注解识别获取到了Controller层的各个Handler请求路径注解后,就执行到了下一行,如下图:

getHandler方式源码


通过注解可以找到所有的Handler,其中所有的Handler就存储在handlerMappings中,于是它就遍历了此工具。随后凭据各自的请求工具获取对应的Handler并判空返回获取到的对应Handler工具。继续向下执行,你还会发现这么一个器械,如下图:

getHandler方式


对,你会发现即将返回的Handler是一个名为HandlerExecutionChain的执行链。其中执行链内包罗了即将返回的handler工具和一个interceptorList聚集,其中聚集内有两个工具,这两个工具就是拦截器。以是,不管是你自己使用了拦截器照样没有使用拦截器(内部底层有拦截器),这些拦截器和handler工具会以一个链条的形式执行(拦截器在前,handler工具在后)。则执行历程是遵照着先执行拦截器,后返回并执行handler工具的顺序。返回了HandlerExecutionChain执行链,那么就要最先执行执行链了!问题来了,事实是谁依次执行拦截器和handler工具呢?如下图:

doDispatch()方式源码


返回执行链后,继续执行就执行到了这一行代码,其注释注释为为当前请求工具寻找一个handler适配器。若是你学过适配器设计模式也许你会更容易明白,没有学过也没有关系,随后的注释你也可以明白的。知道了它要为请求工具寻找适配器,那么我们继续执行,就得到了如下的信息:

getHandlerAdapter方式源码


执行流程进入到了getHandlerAdapter方式,远远看到这个方式有一种似曾相识的感受,对,它和HandlerMapping映射器很像,简直就是孪生兄弟。该方式要凭据当前返回的handler工具,为其handler工具寻找一个适配器,而handlerAdapters聚集工具中就存储着三个适配器,想想我们在映射器中获取执行链的时刻是不是也三个呢?对的,他们是成对泛起的,handler的工具找其对应的适配器才可以继续执行下去。找到与当前handler工具成对的适配器之后,就返回了该适配器。适配器返回后中心经由了如下方式:

doDispatch()方式源码


中心经由了这一段代码,获取了请求工具的请求方式并对此举行了一系列的判断操作。继续执行到了下面,下面有一个if判断,判断执行了applyPreHandler方式,此方式就是拦截器的前置方式。执行完拦截器的前置方式后,继续向下执行,这时刻就该执行如下代码:

doDispatch()方式源码


今后方式可见ha工具是此时的handler工具,说明在执行handler工具之前执行了拦截器,这也是遵照了执行链的顺序。继续执行下去,将完成了请求参数工具的封装和响应中Json字符串与工具的转换后,返回了一个mv工具。那么mv工具是什么呢?其实是在上面界说的ModelAndView工具。返回mv工具后,继续执行便执行到了如下主要的执行逻辑:

doDispatch()方式源码


其中在执行历程中,判断并执行了拦截器的后置方式。执行完后置方式后,举行了一系列的判断,就最先执行了processDispatchResult(processdRequest, response, mappdeHandler, mv, dispatchException)方式,该方式中携带了请求工具、响应工具、handler工具、ModelAndView工具等,进入到此方式源码中,你会发现他举行了一系列的判断,通过如下方式对ModelAndView工具举行了渲染:

render方式源码


对ModelAndView工具举行渲染和视图剖析后,继续跟进方式,由于胜利马上就要来临了。如下图:

render方式源码


继续执行,就会发现它最先通过resolveViewName方式来剖析视图了。于是,就进入到了该方式,如下图:

resolveViewName方式源码


首先,看到此方式的源码,你可以发现,viewResolvers视图剖析器会剖析ModelAndView工具,并返回了一个View工具。厥后View工具也会被一个名叫render的方式渲染,如下:

view.render()


可见,此View工具并不简朴,它执行了一番事后,由于我的网页跳转时使用的请求转发,于是就到了如下页面源码:

InternalResourceView源码


点击此方式就会发现我们熟悉的请求转发了,此时它在这里读取剖析了spring-mvc.xml设置文件,为内部默认的请求转发拼接好了路径forward:/XXX/XXX(此时也剖析了spring-mvc.xml设置文件内的其他组件),如下图:

请求转发(InternalResourceView.java)


若是是重定向呢,那么就是如下类中的重定向方式,如下图所示:

重定向(RedirectView.java)


随后,转发或重定向跳转至JSP页面(视图层)后,渲染数据到HTML中,并渲染完HTML内容后,输出给浏览器并作出响应,在浏览器中显示!

此SpringMVC我以打断点调试的方式走了一遍底层的执行流程。我相信你自己打断点调试也会有一个不错的收获!

SpringMVC的内部组件

HandlerMapping(处置器映射器)HandlerAdapter(处置器适配器)ViewResolver(视图剖析器)

SpringMVC默认组件初始化加载

上面我们通过Debug简朴的走了一遍SpringMVC的执行流程,然则前面所说的那么多内部组件是怎么来的呢?于是,我从DispatherServlet找到了一个方式initStrategies,如下:

initStrategies方式源码


在执行流程最先之前,做了内部组件的一系列初始化操作,这里我们以initHandlerMappings方式举行追溯,找到 SpringMVC 的默认设置文件。进入 initHandlerMappings 方式,由于我们并没有举行设置(注解或者 Bean 标签),以是该方式中的前两种情形都市跳过,会来到最下面的默认情形处,调用了 getDefaultStrategies 方式,读取默认的设置文件。

initHandlerMappings方式源码


getDefaultStrategies方式源码


在 getDefaultStrategies 方式中,有一个 defaultStrategies,我们当该类上面看一下,如下图:

defaultStrategies源码


这里就是举行加载默认设置文件的地方,点击 DEFAULT_STRATEGIES_PATH 常量,找到了默认的设置设置文件。

DEFAULT_STRATEGIES_PATH常量


于是我想办法翻到了这个设置文件,内里就初始化了种种组件,人人可以查阅:

DispatcherServlet.properties设置文件