以前已经分析过了Spring的IOC(《零基础带你看Spring源码——IOC控制反转》)与AOP(《从源码入手,一文带你读懂Spring AOP面向切面编程》)的源码,本次就来分析下SpringMVC。本文先简述下目前SpringMVC的使用状况,而后经过Demo的简单让你们有一个初步的使用印象,而后带着印象去看其中执行的分发源码。前端
Spring MVC,官方名字实际上是Spring Web MVC,Maven上的包名也是spring-webmvc。从Spring诞生以来,它就是一款基于Servlet Api的web架构。值得一提的是,在Spring5的时候,出了一款新的Web架构,Flux,是基于事件驱动模型(相似nodejs)作的。之后会写一篇来专门介绍一下Flux,敬请关注。node
MVC,能够说是“上个世纪”最流行的先后端交互模型。它包含Model(业务模型)、View(用户视图)、Controller(控制器),把各部分分开组织,对代码抽象与隔离的处理可谓是代码设计的典范。web
不过自从15年开始,随着各类前端框架的崛起,使得前端后端的关系发生进一步的演变,从MVC架构演变成先后端分离的REST架构了。之前MVC架构每次请求都须要通过控制器->模型->视图的流程,演变成前端请求后端接口,返回JSON的这样一种REST架构。
<!-- more -->
问题来了,咱们到底还在用SpringMVC吗?答案是,不全用。先后端作了代码以及部署的分离,也就是说后端并不感知前端的存在,因此对于后端而言,View(用户视图)也就无从可谈了。Model(业务模型)发送性质上的改变,之前是一个前端所须要的Model,给页面读取,如今是一个JSON格式给到前端,由前端自由处理。spring
而做为Web框架的核心,Controller(控制器)则是依然留存的。因此如今你们用SpringMVC用的更可能是Controller这一层。固然SpringMVC还有其余组件,包括filter、Http Caching、Web Security等等。本文只是着重MVC架构中的Controller的功能,而Controller的核心组件则是DispatcherServlet。因此后面咱们将经过Demo,来逐步深刻了解下,DispatcherSevlet如何作到对请求控制分发的。编程
在传统的SpringMVC中,须要配置web.xml和applicationContext.xml。前者是负责配置项目初始化的配置,如servlet、welcome页面等,是JavaEE的规范。后者是初始化Spring Context的配置,主要是Bean的配置。后端
前文说到,SpringMVC是基于Servlet的架构,而DispatcherServlet则是SpringMVC拦截处理全部请求的Servlet,因此web.xml须要配置DispatcherServlet。其余的还有contextLoaderListener,负责加载除DispatcherServlet外的全部context内容,另外还须要经过contextConfigLoader指定Spring的配置文件(如applicationContext.xml)。浏览器
那么在项目启动的时候,加载web.xml首先会执行contextLoaderListener,让它初始化好Spring的Application context。后面有HTTP请求进来,则会落到DispatcherServlet上,让它去作处理分发。tomcat
自从Spring配置注解和SpringBoot诞生以来,愈来愈少人去写web.xml和applicationContext.xml配置文件了。但为了方便直接了解Dispatcher的原理,Demo直接用SpringBoot的starter一键式搭建。前端框架
直接添加web的starter依赖微信
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.0.4.RELEASE</version> </dependency>
看下这个starter包含什么内容
绿框是springMVC的依赖,红框是Spring自动配置的依赖,蓝框则是内嵌tomcat的依赖。里面Spring的版本是5.0.8 RELEASE的。
SpringBoot启动类
测试controller
启动项目后,在浏览器里面输入http://localhost:8080/hello?name=Zack
。结果返回Hello Zack
。
以上就是咱们如今利用SpringMVC的基本内容,下面咱们来看下SpringMVC如何利用DispatcherServlet作拦截分发的。
当一个请求进来的时候,会先执行各类filter,过滤掉最终须要的请求,而后会落到DispatcherServlet中的doService()方法。该方法是预先设置一些特殊请求参数,而后再转发给doDispatch()作真正的处理转发。
看一下doDispatch()的注释说明
该方法的做用就是执行实际分发到的handler。
从注释可知,整个的分发逻辑核心,就在于HandlerAdapter和Handler。那这两究竟是什么东西?
官网上的说明
HandlerAdapter协助DispatcherServlet去调用对应的handler,忽略具体handler是怎么调用的。例如调用注解形式的controller须要处理注解,xml配置形式的要解析配置文件。这个适配器就是为了帮助DispatcherServlet屏蔽掉处理具体的细节。
至于Handler没有清晰解释,但咱们debug源码能够发现,Handler其实就是实际分配到具体须要去处理的方法(对比下图红框和上面Demo的controller)。
回到doDispatch()这个方法的源码上,看到getHandler()、getHandlerAdapter()就是获取Handler和HandlerAdapter所在。
看下getHandler()源码
整个方法就那么几行,不过须要注意有两个点。一个是该方法是返回HandlerExecutionChain类型,而不是一个Handler。
HandlerExecutionChain其实就是Handler的一层封装,还包含Handler对应的interceptor拦截器,用于执行Handler的一些前置和后置的操做。
另一个点,HandlerExecutionChain是按顺序遍历handlerMappings拿出来的。那HandlerMapping又是什么呢?
从官网说明可知,它是一个请求和handler(实际是HandlerExecutionChain)的关联Map,通俗的说就是路由与处理逻辑的关联。它主要有两个实现,一个是RequestMappingHandlerMapping(支持注解形式方法),另外一个是SimpleUrlHandlerMapping(维护显示注册的URI资源)。
由此可推测,在Spring启动的时候,就会去扫描注解、注册的静态资源,从而初始化这个handlerMappings。具体逻辑就在DispatcherServlet中的initHandlerMappings方法内。
初始化的方法内,主要有三步:
对于第三点的默认策略,能够找到DispatcherServlet.properties这个文件,里面配置了一些默认HandlerMapping、HandlerAdapter等相关类。
在初始化handlerMappings后,若是有请求进来,后面的request就用请求的路由与HandlerMapping对比,最后找出Handler(HandlerExecutionChain)。
在取出实际处理的Handler后,就须要用它找出support它的适配器(HandlerAdapter)。按照前面对HandlerAdapter的描述,对于Demo而言,support这个Handler一定是RequestMappingHandlerAdapter。
这个逻辑也很是简单,一样是遍历已初始化的handlerAdapters(初始化的过程相似handlerMappings),而后对于具体每一个handlerAdapter,调用其support()方法,看是否支持。
supports()方法也很简单,就用instanceof判断handler是否Adapter本身支持的类。
在获取完Handler和HandlerAdapter后,就能够执行HandlerAdapter中的handle方法,其实际只是调用Handler的方法。
咱们按Demo例子,看下HttpRequestHandlerAdapter的handle()方法实现。
这个方法里面就是用HttpServlet的Request和Reponse去调用咱们本身写的controller里面的方法。须要注意的是,这个方法返回的是ModelAndView,但咱们目前基于Rest架构是已经不用的了,因此方法返回null回去了。
前面提到Handler是被封装在HandlerExecutionChain里面的,其中还包含一些前置后置的拦截器。因此在执行HandlerAdapter.handle()先后会有对HandlerExecutionChain的调用,执行interceptor对先后置处理的方法
具体里面的实现就是执行interceptor的preHandle()和postHandle()方法。
回过头来想下,这里的先后置处理会包括什么呢?在HandlerInterceptor注解上有说明三个实现类,分别是UserRoleAuthorizationInterceptor(检查用户权限)、LocaleChangeInterceptor(修改本地时间)、ThemeChangeInterceptor(修改当前主题)。能够看出HandlerInterceptor基本都是对请求的一些预处理和结果封装。
以上就是SpringMVC中DispatcherServlet的基本过程。下面来总结下以上内容:
更多技术文章、精彩干货,请关注
博客:zackku.com
微信公众号:Zack说码