SpringMVC中的DispatcherServlet请求分析
一、service请求(servlet请求转换为Http请求)
DispatcherServlet作为一个Servlet,那么当有请求到Tomcat等Servlet服务器时,会调用其service方法。再调用到其父类GenericServlet的service方法,HttpServlet中实现,如下(开始请求的调用):
@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) res; } catch (ClassCastException e) { throw new ServletException(lStrings.getString("http.non_http")); } service(request, response); }
HttpServlet层:将request和response类型进行转换后,继续调用service方法:
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); } catch (IllegalArgumentException iae) { // Invalid date header - proceed as if none was set ifModifiedSince = -1; } if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
根据调用的Http请求的方式,调用具体的底层(FrameworkServlet层)方法,get、post请求等都会有相同的处理,比如doGet如下:
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); publishRequestHandledEvent(request, response, startTime, failureCause); } }
主要的核心逻辑为doService,但是Tomcat是使用线程池的方式接受来自客户端的请求的,当前请求中可能带有Locate(国际化参数信息),那么需要使用ThreadLocal在请求前记录参数信息,在请求之后finally中将参数恢复回去,不会影响到下一个请求。
Spring经常会这样进行处理,比如AopContext等处理Aop切面信息。
二、doService请求(request中添加SpringMVC初始化的九大件信息)
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request); // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. 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()); if (this.flashMapManager != null) { 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()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
1、打印请求日志
2、请求中添加属性(WebApplicationContext容器,i18n解析器,主题解析器,主题,重定向属性处理)
3、核心方法 doDispatch
三、doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; // 异步请求属性解析(重定向) WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 如果是Multipart上传文件请求,则调用multipartResolver.resolveMultipart(上传文件解析器进行解析) processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 确定Handler处理该请求(HandlerExecutionChain调用链,责任链模式) mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根据初始化时加载的适配器挨个匹配是否能适配该调用链 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 对请求头中的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; } } // 对HandlerExecutionChain中的所有HandlerInterceptor调用preHandle方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 真正的请求调用 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } // 判断Controller返回的ModelAndView中是否有View, // 没有则使用viewNameTranslator执行没有试图的解析规则 applyDefaultViewName(processedRequest, mv); // 对HandlerExecutionChain中的所有HandlerInterceptor调用postHandle方法 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); } // 处理结果解析(ModelAndView或者Exception),保证最终会执行所以Interceptor的triggerAfterCompletion 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()) { // 不知道什么情况下回进入相当于执行所以Interceptor的postHandle和afterCompletion方法 if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // 清除当前文件上传请求的资源 if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
这里是整个SpringMVC的核心,每个流程都可能会比较复杂,后续单独分析。流程如下(主要是标红的步骤):
1、异步请求属性解析(重定向)
2、如果是Multipart上传文件请求,则调用multipartResolver.resolveMultipart(上传文件解析器进行解析)
4、确定Handler处理该请求(HandlerExecutionChain调用链,责任链模式)
5、根据初始化时加载的适配器挨个匹配是否能适配该调用链
6、对请求头中的last-modified进行处理
7、对HandlerExecutionChain中的所有HandlerInterceptor调用preHandle方法
8、真正的请求调用
9、没有试图的解析返回
10、对HandlerExecutionChain中的所有HandlerInterceptor调用postHandle方法
11、处理结果解析(ModelAndView或者Exception),保证最终会执行所以Interceptor的triggerAfterCompletion
12、清除当前文件上传请求的资源
HandlerInterceptor方法调用
当我们需要使用Spring的拦截器时,会集实现HandlerInterceptor的preHandle、postHandle、triggerAfterCompletion方法。
当第4步完成后我们获取到了HandlerExecutionChain调用链,其中包括需要执行的拦截器和真正调用的方法(后面专门分析专门获取的)。
但是在上面的步骤看到了对拦截器的三个方法的调用时机,其实每个调用的方法都差不多,就看preHandle方法即可,调用HandlerExecutionChain的applyPreHandle方法如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
由于preHandle方法允许返回false则不执行真实的Controller方法调用,所以需要每次判断。但是在执行preHandle和postHandle方法时,都允许最后调用一次triggerAfterCompletion方法。都是在拿到调用链中的所以有序的拦截器,轮训调用其对应的方法即可。
到此这篇关于SpringMVC中的DispatcherServlet请求分析的文章就介绍到这了,更多相关DispatcherServlet请求分析内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Java 并发编程学习笔记之Synchronized底层优化
这篇文章主要介绍了Java 并发编程学习笔记之Synchronized底层优化的相关资料,主要包含了重量级锁,轻量级锁,偏向锁和其他优化等方面,有需要的小伙伴可以参考下2016-05-05Windows系统下Java连接SQL Server的方法简介
这篇文章主要介绍了Windows系统下Java连接SQL Server的方法,分别是JDBC和JTDS的相关使用,需要的朋友可以参考下2015-09-09应用启动数据初始化接口CommandLineRunner和Application详解
这篇文章主要介绍了应用启动数据初始化接口CommandLineRunner和Application详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-12-12使用graalvm为带有反射功能的java代码生成native image的示例详解
graalvm让native镜像支持反射的关键是利用json提前告诉它哪些类的哪些方法会被反射调用,然后它就能力在运行时支持反射了,这篇文章主要介绍了如何使用graalvm为带有反射功能的java代码生成native image,需要的朋友可以参考下2024-02-02
最新评论