- 2023-04-26 12:14:38
- 2792 热度
- 0 评论
前面無名和大家聊了 DispatcherServlet 的父类 FrameworkServlet,大家从中了解到在 DispatcherServlet 中,方法执行的入口应该是 doService。如果小伙伴们还没看前面的分析,可以先看下,这有助于理解本文,传送门SpringMVC 源码分析之 FrameworkServlet。
即使你没看过 DispatcherServlet 的源码,估计也听说过:DispatcherServlet 是 SpringMVC 的大脑,它负责整个 SpringMVC 的调度工作,是 SpringMVC 中最最核心的类,SpringMVC 整个顶层架构设计都体现在这里,所以搞明白 DispatcherServlet 的源码,基本上 SpringMVC 的工作原理也就了然于胸了。
经过上篇文章的分析,大家已经知道 DispatcherServlet 的入口方法是 doService,所以今天我们就从 doService 方法开始看起,無名将带领大家,一步一步揭开 DispatcherServlet 的面纱。
doService
先来看 doService,把源码先贴上来,然后我们逐步分析:
1 |
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { |
这里的代码并不长,我们来稍微分析一下:
- 首先判断当前请求是不是 include 请求,如果是 include,则对 request 的 attribute 做一个快照备份,在最后的 finally 中再对备份的属性进行还原。
- 接下来对 request 设置一些常见属性,例如应用上下文、国际化的解析器、主题解析器等等,这些东西在初始化的时候已经准备好了,这里只是应用(初始化过程参见SpringMVC 初始化流程分析一文)。
- 接下来处理 flashMap,如果存在 flashMap 则进行复原,这一块無名在之前的文章中和小伙伴们已经分享过了,传送门SpringMVC 中的参数还能这么传递?涨姿势了!。
- 接下来处理 RequestPath,将请求路径对象化以备后续使用(在后面的请求映射匹配时会用到)。
- 调用 doDispatch 方法进行下一步处理。
- 还原快照属性、还原 RequestPath。
所以说这段代码并不难理解,它的核心在于 doDispatch 方法,所以接下来我们就来看看 doDispatch 方法。
doDispatch
doDispatch 方法所做的事情就比较多了,我们来看下:
1 |
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { |
这个方法比较长,涉及到很多组件的处理,这里無名先和大家把思路梳理畅通,各个组件的详细用法無名将在以后的文章中和大家仔细分享。
doDispatch 方法其实主要做了两方面的事情:请求处理以及页面渲染,我们先来看看初始变量的含义:
- processedRequest:这个用来保存实际上所用的 request 对象,在后面的流程中会对当前 request 对象进行检查,如果是文件上传请求,则会对请求重新进行封装,如果不是文件上传请求,则继续使用原来的请求。
- mappedHandler:这是具体处理请求的处理器链,处理器链包含两方面的东西:请求处理器和对应的 Interceptor。
- multipartRequestParsed:表示是否是文件上传请求的标记。
- asyncManager:这是一个异步请求管理器。
- mv:这是最终渲染返回的 ModelAndView 对象。
- dispatchException:表示请求处理过程中所抛出的异常,这个异常不包括渲染过程抛出的异常。
接下来再来看看具体的处理逻辑:
- 首先通过 checkMultipart 检查是不是文件上传请求,如果是,则对当前 request 重新进行包装,如果不是,则直接将参数返回。
- 如果 processedRequest 不等于 request,则说明当前请求是文件上传请求(request 在 checkMultipart 方法中被重新封装了),否则说明当前请求不是文件上传请求。
- 根据当前请求,调用 getHandler 方法获取请求处理器,如果没找到对应的请求处理器,则调用 noHandlerFound 方法抛出异常或者给出 404。
- 接下来再调用 getHandlerAdapter 方法,根据当前的处理器找到处理器适配器。
- 然后处理 GET 和 HEAD 请求头的 Last_Modified 字段。当浏览器第一次发起 GET 或者 HEAD 请求时,请求的响应头中包含一个 Last-Modified 字段,这个字段表示该资源最后一次修改时间,以后浏览器再次发送 GET、HEAD 请求时,都会携带上该字段,服务端收到该字段之后,和资源的最后一次修改时间进行对比,如果资源还没有过期,则直接返回 304 告诉浏览器之前的资源还是可以继续用的,如果资源已经过期,则服务端会返回新的资源以及新的 Last-Modified。
- 接下来调用拦截器的 preHandle 方法,如果该方法返回 false,则直接 return 掉当前请求(拦截器的用法大家可以参考無名之前录的免费的 SpringMVC 视频教程,里边有讲,传送门硬核!無名又整了一套免费视频,搞起!)。
- 接下来执行
ha.handle
去调用真正的请求,获取到返回结果 mv。 - 接下来判断当前请求是否需要异步处理,如果需要,则直接 return 掉。
- 如果不需要异步处理,则执行 applyDefaultViewName 方法,检查当前 mv 是否没有视图,如果没有(例如方法返回值为 void),则给一个默认的视图名。
- 接下来调用 applyPostHandle 方法执行拦截器里边的 postHandle 方法。
- 接下来调用 processDispatchResult 方法对执行结果进行处理,包括异常处理、渲染页面以及执行拦截器的 afterCompletion 方法都在这里完成。
- 最后在 finally 代码块中判断是否开启了异步处理,如果开启了,则调用相应的拦截器;如果请求是文件上传请求,则再调用 cleanupMultipart 方法清除文件上传过程产生的一些临时文件。
这是 doDispatch 方法的一个大致执行逻辑,doDispatch 里边的 try-catch 有两层,最里边那一层,抛出来的异常会被赋值给 dispatchException 变量,这些异常最终在 processDispatchResult 方法中被处理掉,外面的异常则是 processDispatchResult 方法在执行的过程中抛出的异常,一般来说主要是页面渲染时候的异常。
processDispatchResult
最后我们再来看下 processDispatchResult 方法的执行逻辑:
1 |
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, |
可以看到,在 processDispatchResult 方法中首先对异常进行了处理,配置好异常对应的 ModelAndView,然后调用 render 方法对页面进行渲染,最后通过 triggerAfterCompletion 方法去触发拦截器的 afterCompletion 方法。
小结
至此,我们就把一个请求的大致流程和大家梳理完了,無名画了一张流程图我们一起来看下:
这下相信大家对 doDispatch 方法比较熟悉了,当然这里还涉及到很多组件,这些组件無名将在后面的文章中和大家逐一进行分析。
- Spring(403)
- Boot(208)
- Spring Boot(187)
- Java(82)
- Cloud(82)
- Spring Cloud(82)
- Security(60)
- Spring Security(54)
- Boot2(51)
- Spring Boot2(51)
- Redis(31)
- SQL(29)
- Mysql(25)
- IDE(24)
- Dalston(24)
- JDBC(22)
- IDEA(22)
- mongoDB(22)
- MVC(22)
- Web(21)
- CLI(20)
- Alibaba(19)
- SpringMVC(19)
- SpringBoot(17)
- Docker(17)
- Git(16)
- Eclipse(16)
- Vue(16)
- ORA(15)
- JPA(15)
- Apache(15)
- Mybatis(14)
- Oracle(14)
- jdk(14)
- Tomcat(14)
- Linux(14)
- HTTP(14)
- XML(13)
- JdbcTemplate(13)
- OAuth(13)
- Nacos(13)
- Pro(13)
- Data(12)
- JSON(12)
- OAuth2(12)
- stream(11)
- int(11)
- Myeclipse(11)
- not(10)
- Bug(10)
- maven(9)
- Map(9)
- Hystrix(9)
- ast(9)
- APP(8)
- Bit(8)
- API(8)
- session(8)
- Window(8)
- Swagger(8)
- Github(7)
- JavaMail(7)
- Cache(7)
- File(7)
- mail(7)
- IntelliJ(7)
- windows(7)
- too(7)
- HTML(7)
- RabbitMQ(6)
- star(6)
- and(6)
- Excel(6)
- Log4J(6)
- pushlet(6)
- apt(6)
- read(6)
- Freemarker(6)
- WebFlux(6)
- JSP(6)
- Bean(6)
- error(6)
- nginx(6)
- Server(6)
- ueditor(6)
- jar(6)
- ehcache(6)
- UDP(6)
- rdquo(5)
- PHP(5)
- Struts(5)
- string(5)
- Syntaxhighlighter(5)
- script(5)
- Tool(5)
- Controller(5)
- swagger2(5)
- ldquo(5)
- input(5)
- Servlet(5)