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

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=默认的viewName
				applyDefaultViewName(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 afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				如果是文件上传请去,最终会清除掉请求中文件。
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

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

                        

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

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值