Spring源码学习:SpringMVC(4)DispatcherServlet请求入口分析

目录

  • 前言
  • HttpServlet &FrameworkServlet
    • HttpServlet #service
    • FrameworkServlet#processRequest
  • DispatcherServlet#doService
    • doDispatch
    • checkMultipart
    • getHandler
      • AbstractHandlerMapping#getHandler
      • RequestMappingInfoHandlerMapping#getHandlerInternal
      • AbstractHandlerMethodMapping#getHandlerInternal
      • HandlerMethod#createWithResolvedBean
      • AbstractHandlerMapping#getHandlerExecutionChain
    • noHandlerFound
    • getHandlerAdapter
    • Last-Modified 的缓存处理
      • SimpleControllerHandlerAdapter#getLastModified
      • RequestMappingHandlerAdapter#getLastModifiedInternal
    • 拦截器三个方法的调用
      • HandlerExecutionChain#applyPreHandle
      • HandlerExecutionChain#applyPostHandle
      • HandlerExecutionChain#triggerAfterCompletion
    • HandlerAdapter#handle
      • handleInternal
      • invokeHandlerMethod
    • applyDefaultViewName
      • getDefaultViewName
    • processDispatchResult
      • processHandlerException
        • AbstractHandlerExceptionResolver#resolveException
        • getExceptionHandlerMethod
      • render
  • 总结

前言

​ 通过前面的分析,我们知道DispatcherServlet其本质还是Servlet,那么当客户端的请求到达时,根据Servlet生命周期,其应该会调用其或者其父类中的service方法。

在其父类FrameworkServlet中我们找到了service方法

	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
   

		/*
		 *	获取HttpMethod类型,
		 *	HttpMethod为枚举类,支持的Http请求类型有GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
		 */
		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
   
			// 若方法为 PATCH 方法或为空则单独处理
			processRequest(request, response);
		}
		else {
   
			super.service(request, response);
		}
	}

这里有两个方法,一个是processRequest(request, response)super.service(request, response),这里的super就是HttpServlet,我们分别看一下

HttpServlet &FrameworkServlet

HttpServlet #service

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
   
        String method = req.getMethod();
		//如果是get
        if (method.equals(METHOD_GET)) {
   
            //lastModified 缓存判断
            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 = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
   
                    // 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 {
   
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            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);
        }
    }

​ 上面几个方法最常用的就是 doGet()doPost() 。这两个方法被 FrameworkServlet 重写了。我们来看看在 FrameworkServlet 中的实现。

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
   
    processRequest(request, response);
}

@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
   
    processRequest(request, response);
}

​ 我们可以很清楚的看到,对于大部分的请求,还是依赖于 HttpServlet#service(HttpServletRequest, HttpServletResponse) 来进行一个请求的分发。对于我们常见的 doGet() 和 doPost() 方法都是直接调用 processRequest(request, response);, 而processRequest方法 的具体实现在FrameworkServlet#processRequest 中 。

FrameworkServlet#processRequest

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
   

		long startTime = System.currentTimeMillis();
		// 记录抛出的异常~~~(若有的话)
		Throwable failureCause = null;

		//国际化设置
    	// 1 提取当前线程的 LocaleContext  属性
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    	// 2. 根据当前的request 创建对应的 LocaleContext ,并绑定到当前线程
		LocaleContext localeContext = buildLocaleContext(request);

		//构建ServletRequestAttributes对象
    	//3. 提取当前线程的 RequestAttributes 属性
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    	//4.根据当前的request 创建对应的 RequestAttributes ,并绑定到当前线程
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		// 拿到异步管理器。这里是首次获取,会new WebAsyncManager(),然后放到request的attr里面
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		//这里需要注意:给异步上下文恒定注册了RequestBindingInterceptor这个拦截器(作用:绑定当前的request、response、local等)
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		//这句话很明显,就是吧request和Local上下文、RequestContext绑定
		initContextHolders(request, localeContext, requestAttributes);

		try {
   
			//5.模版设计模式:由子类DispatcherServlet去实现实际逻辑
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
   
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
   
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
   
			//这个时候已经全部处理完成,视图已经渲染了
			//doService()方法完成后,重置上下文,也就是解绑
            // 6. 请求结束,恢复线程原状
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
   
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			//关键:不管执行成功与否,都会发布一个事件,我处理了这个请求(有需要监听的,就可以监听这个事件了,每次请求都会有)
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

​ 由于逻辑都被封装到 doService(request, response); 中,所以这里还是比较简单。而doService又被 DispatcherServlet实现了。因此我们这里来看看 DispatcherServlet#doService

DispatcherServlet#doService

@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.
		 /*
		 * 如果当前请求是一个 include request,如:
		 * 则为此请求属性建立快照,以便include request结束后能够将其恢复
		 */
		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.
		// 说得很清楚,把一些常用对象放进请求域  方便Handler里面可以随意获取
		// Spring上下文
		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 {
   
			// 【重要】 真正开始处理http请求
			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);
				}
			}
		}
	}

​ 很明显,这 “祖孙三代” 一层一层传递,终于传递到了DispatcherServlet 手里,这里我们直接开始看doDispatch(request, response);,这里才是核心逻辑的所在!!!

doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
		// 此处用processedRequest  需要注意的是:若是处理上传,processedRequest 将和request不再指向同一对象
		HttpServletRequest processedRequest = request;
		// 链处理器
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
   
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
   
				//1.checkMultipart 判断是否是上传需求。且看下面的具体分析:
				//如果请求是POST请求,并且请求头中的Context-Type是以multipart/开头的就认为是文件上传的请求
				processedRequest = checkMultipart(request);
				// 标记一下:是否是文件上传的request了
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				// 2.查找当前请求对应的handler,包括Handler(控制器)本身和Handler拦截器
				mappedHandler = getHandler(processedRequest);
				// 未能找到对应的handler,抛出NoHandlerFoundException异常并返回404
				if (mappedHandler == null) {
   
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 3.查找当前请求对应的HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				// 4.处理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;
					}
				}

				// 5.应用前置拦截器:执行注册拦截器的preHandle方法
				// 如果有拦截器返回false,则表明该拦截器已经处理了返回结果,直接返回;
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
   
					return;
				}

				// Actually invoke the handler.
				// 6.调用HandlerAdapter的handler方法,真正开始处理Controller
				// *****真正执行我们自己书写的controller方法的逻辑。返回一个ModelAndView
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				// 7.如果当前请求是异步处理,直接返回
				// 如果异步启动了,这里就先直接返回了,也就不会再执行拦截器PostHandle之类的
				if (asyncManager.isConcurrentHandlingStarted()) {
   
					return;
				}

				//8. 为返回值设定默认视图名,如果当前返回值中不包含视图名的话
				applyDefaultViewName(processedRequest, mv);

				// 9.应用已注册拦截器的后置方法:执行所有的拦截器的postHandle方法,并且把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);
			}

			// 10.处理分发调用结果,如视图模型解析、返回等工作
			// 处理返回结果,包括处理异常、渲染页面,发出完成通知触发Interceptor的afterCompletion
			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);
				}
			}
		}
	}

checkMultipart

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
   
		if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
   
			if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
   
				if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
   
					logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
				}
			}
			else if (hasMultipartException(request)) {
   
				logger.debug("Multipart resolution previously failed for current request - " +
						"skipping re-resolution for undisturbed error rendering");
			}
			else {
   
				try {
   

你可能感兴趣的:(spring,学习,java,后端,开发语言)