当前位置: 代码迷 >> 综合 >> spring基本使用(19)-springMVC3-SpringMVC的前端控制DispatcherServlet的工作原理
  详细解决方案

spring基本使用(19)-springMVC3-SpringMVC的前端控制DispatcherServlet的工作原理

热度:44   发布时间:2023-10-24 15:44:12.0

1、前面我们花了两篇文章把SpringMVC的初始化流程讲了一遍,今天我们正式进入到SpringMVC的各大组件剖析,我们第一剖析的就是这个SpringMVC的前端控制器DispatcherServlet。

 

2、DispatcherServlet工作流程解析

      2.1、DispatcherServlet是一个标准的Servlet, 那么它肯定实现的Servlet的标准方法:

             void service(ServletRequest var1, ServletResponse var2)

              我们了解其实现的整个流程得出,我们所有的请求都会在DispatcherServlet的doService方法中处理

             doService(HttpServletRequest request, HttpServletResponse response)

               DispatcherServlet的doService方法源码:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {if (this.logger.isDebugEnabled()) {String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");}Map<String, Object> attributesSnapshot = null;if (WebUtils.isIncludeRequest(request)) {attributesSnapshot = new HashMap();Enumeration attrNames = request.getAttributeNames();label108:while(true) {String attrName;do {if (!attrNames.hasMoreElements()) {break label108;}attrName = (String)attrNames.nextElement();} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));attributesSnapshot.put(attrName, request.getAttribute(attrName));}}向请求中添加当前的SpringMVC应用上下文,这样我们在Controller中可以中获取如下方式:/*WebApplicationContext attribute = (WebApplicationContext) request.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE);*/request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());同样的方式把DispatcherServlet的本地信息解析器localeResolver也设置到请求中request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);把DispatcherServlet的主题解析器也添加到请求中request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);把DispatcherServlet的主题源设置到请求中。request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());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方法去处理请求,此处很重要!!!!this.doDispatch(request, response);} finally {if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {this.restoreAttributesAfterInclude(request, attributesSnapshot);}}}

                     在doService方法中调用了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 {1、解析是否有文件上传,检查的方式就是使用multipartResolver文件上传解析器根据请求去解析,具体怎么解析后面详细说multipartResolver组件的时候会分析。processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.2、获取一个包装过的处理器,获取的方式是使用DispatcherServlet以及初始化好的HandlerMapping处理器映射器列表去获取,有多个HandlerMapping的话,循环调用,一旦获取到处理器直接返回且结束循环。具体怎么获取在讲解HandlerMapping组件的时候会详细剖析。!!!重点强调一下,什么是包装过的处理器,其实就是HandlerExecutionChain 这个类中有SpringMVC的拦截器列表,还有一个很重要的成员属性private final Object handler; 这个就是真正意义上的处理器,类型是一个Object,因为在不同处理器映射器中,对处理器的定义是不一样的,比如RequestMappingHandlerMapping中此类型就是HandlerMethod也就是Controller中的每一个方法描述。mappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {如果使用HandlerMappings处理器映射器列表都没找到处理器,就调用noHandlerFound方法,这个方法会将response.sendError(404); 哈哈 头疼的404原来是在这里设置的啊noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.3、如果使用处理器映射器找到了包装过的处理器,那就使用真正的处理器(也就是被包装的处理器)去寻找一个处理器适配器,寻找的方式就是循环的看看DispatcherServlet的handlerAdapters列表有没有支持当前处理器的,一旦有支持的就直接返回处理器适配器且结束循环。如果没有那就直接抛出异常ServletException。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 (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}校验请求是否被篡改过,如果被篡改过就结束请求。if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}使用包装过的处理器去执行SpringMVC的拦截器的前置方法,如果有其中一个拦截器返回的false就直接结束请求。if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.4、使用获取到的处理器适配器HnadlerAdapter去执行真正的处理器,返回一个ModelAndView实例。mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}5、如果返回的ModelAndView中没有view,那就设置ModelAndView实例的view=默认的viewNameapplyDefaultViewName(processedRequest, mv);6、使用包装过多处理器去执行SpringMVC的拦截器的后置方法,后置方法执行所有的拦截器的后置方法。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);}7、处理处理器执行结果,如果处理器执行抛出异常,也是在此处处理。并且也会在此步骤去执行包装过的处理器中的所有拦截器的afterCompletion方法,执行方式for循环调用全部。processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {如果包装过的整个处理链路任何一个环节出现异常,都会执行包装过的处理器里的SpringMVC拦截器的afterCompletion方法,执行方式for循环调用全部。triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {如果包装过的整个处理链路任何一个环节出现系统级别的错误,都会执行包装过的处理器里的SpringMVC拦截器的afterCompletion方法,执行方式for循环调用全部。triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.如果是文件上传请去,最终会清除掉请求中文件。if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}

      以上就是SpringMVC的前端控制DispatcherServlet的整体工作原理,这个与我们之前描述SpringMVC的整体工作流程的图片是对应的,如下:

                        

在下一篇文章中,我们将会分析文件上传解析器。

 

 

 

  相关解决方案