写点什么

初探 DispatcherServlet#doDispatch

  • 2021 年 11 月 11 日
  • 本文字数:2859 字

    阅读完需:约 9 分钟

  • themselves to decide which methods are acceptable.

  • @param request current HTTP request

  • @param response current HTTP response

  • @throws Exception in case of any kind of processing failure


*/


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


//将 request 对象重新存储到 processedRequest


HttpServletRequest processedRequest = request;


//处理器链


HandlerExecutionChain mappedHandler = null;


boolean multipartRequestParsed = false;


//获取异步请求管理器


WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);


try {


//最终返回的 ModelAndView 对象


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) {


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 (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;


}


catch (Throwable err) {


// As of 4.3, we're processing Errors thrown from handler methods as well,


// making them available for @ExceptionHandler methods and other scenarios.


dispatchException = new NestedServletException("Handler dispatch failed", err);


}


processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);


}


catch (Exception ex) {


triggerAfterCompletion(processedRequest, response, mappedHandler, ex);


}


catch (Throwable err) {


triggerAfterCompletion(processedRequest, response, mappedHandler,


new NestedServletException("Handler processing failed", 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);


}


}


}


}


0x02 前期处理




前面部分代码是对请求的一些前期处理,从processedRequest = checkMultipart(request);开始进入对 request 的处理逻辑部分。


首先 check 该 request 是否为文件上传请求,如果是则重新封装 request,不是则把传入的 request 原封不动 return 回来



之后判断我们的 requst 是否在 checkMultipart 方法中封装过(即 request 是文件上传请求),判断的布尔值结果赋值给 multipartRequestParsed,此值类似于 flag 用作后面判断,当是文件上传请求时在最后会清除文件上传过程中的临时文件。



0x02 getHandler




之后进入 Handler 部分,调用 org/springframework/web/servlet/handler/


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


AbstractHandlerMapping#getHandler 并返回 executionChain 赋值给 mappedHandler,如果没找到对应的 handler 和拦截器就会进入 if 中调用 noHandlerFound 抛出异常。



org/springframework/web/servlet/handler/AbstractHandlerMapping#getHandlerExecutionChain 实现:



简而概之,这里返回值 executionChain 中封装了 2 个重要的东西,之后会在 doDispatch 中被用到:


  1. 处理当前请求的 Controller 及其方法的信息

  2. 当前请求所需要的拦截器信息



0x03 getHandlerAdapter




下面调用 getHandlerAdapter 根据之前返回的 executionChain 拿到 handler,再根据 handler 获取适配的 handlerAdapter 处理器适配器



这里缺省为 RequestMappingHandlerAdapter 优先级最高,最终返回的也是它。



0x04 Last_Modified 处理




之后处理 GET 和 HEAD 请求头的 Last_Modified 字段。


当浏览器第一次发起 GET 或者 HEAD 请求时,请求的响应头中包含一个 Last-Modified 字段,这个字段表示该资源最后一次修改时间,以后浏览器再次发送 GET、HEAD 请求时,都会携带上该字段,服务端收到该字段之后,和资源的最后一次修改时间进行对比,如果资源还没有过期,则直接返回 304 告诉浏览器之前的资源还是可以继续用的,如果资源已经过期,则服务端会返回新的资源以及新的 Last-Modified



0x04 applyPreHandler




接下来做了一个判断,调用 applyPreHandler()方法对所有的拦截器进行遍历,如果发现拦截器的 preHandle()方法返回 false 的话,则直接执行 triggerAfterCompletion()方法,并返回 false,运行停止,如果获取的布尔类型为 true,则将对 interceptorIndex 进行赋值为 1




0x05 handle




之后是 handlerAdaptor 调 handle,去进行对 handler 的一个处理



这里的 chain 比较复杂


org/springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter#handle


org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter#handleInternal


org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter#invokeHandlerMethod


org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod#invokeAndHandle


org/springframework/web/method/support/InvocableHandlerMethod#invokeForRequest


跟进到 org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java#invokeAndHandle 方法,这里调用 invokeFoRequest 会返回 returnValue,该方法会根据输入的 uri,调用相关的 controller 的方法获取返回值,并将其返回给returnValue,作为待查找的模板文件名,再去传给视图解析器处理。(这里因为我用的 Controller 方法中没有返回值,所以 returnValue 为 null)



最终层层返回值赋值给 mv



0x06 异步请求处理




下一步判断是否需要进行异步处理请求,需要的话 return 掉



0x07 applyDefaultViewName




接下来 applyDefaultViewName 方法判断当前视图是否为空,如果为空,调用 getDefaultViewName 方法获取 ModelAndView



评论

发布
暂无评论
初探DispatcherServlet#doDispatch