天天看點

Http請求處理流程源碼解析

作者:路過一排樹

http請求流程

http請求的處理過程大緻為:

Http請求處理流程源碼解析

那麼伺服器端web應用程式是如何進行處理的呢?下面讓我們一起探索其中的原理,以下是一些個人了解,歡迎大家讨論和交流

  1.Tomcat是Web應用伺服器,是一種Servlet/JSP容器,是以,所有的請求都會從DispatcherServlet進行處理,首先請求會到達`doService`方法,該方法會調用doDispatch處理實際的請求。

@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;
        // 确定給定的請求是否是包含請求,即不是來自外部的頂級HTTP請求。
		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());
		// 放入LocaleResolver,作用是解析Request中的語言标志參數或head中的Accept-Language參數,并将解析的結果儲存到指定的域中,此處放入的是AcceptHeaderLocaleResolver
        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);
		}

		RequestPath previousRequestPath = null;
		if (this.parseRequestPath) {
			previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
			ServletRequestPathUtils.parseAndCache(request);
		}

		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);
				}
			}
			if (this.parseRequestPath) {
				ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
			}
		}
	}           

  2.doDispatch方法,先嘗試擷取處理目前請求的請求處理鍊,如果沒有擷取,則抛出異常;否則,執行請求處理鍊,ConversionServiceExposingInterceptor->ResourceUrlProviderExposingInterceptor

此處

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 {
                // 如果請求是一個檔案上傳請求,則對請求進行封裝解析出相關參數;否則,什麼也不做,傳回原請求參數
				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.
                // 擷取處理請求的擴充卡,RequestMappingHandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = HttpMethod.GET.matches(method);
				if (isGet || HttpMethod.HEAD.matches(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.
                // 執行請求處理,調用AbstractHandlerMethodAdapter類中的handle方法
				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 ServletException("Handler dispatch failed: " + err, err);
			}
			// 處理modelAndView,如果出現異常,則處理異常,抛出;否則,反向執行請求處理鍊中的afterCompletion方法
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new ServletException("Handler processing failed: " + err, 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);
				}
			}
		}
	}           

  3.AbstractHandlerMethodAdapter#handleInternal方法。handle方法會調用内部的handleInternal方法

protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ModelAndView mav;
        // 校驗請求類型和會話是否合法,如果沒有設定,則不校驗
		checkRequest(request);

		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) {  // 同步阻塞請求
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				// No HttpSession available -> no mutex necessary
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			// No synchronization on session demanded at all...
            // 非同步阻塞請求,實際調用此處
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				// 對響應内容進行預處理
				prepareResponse(response);
			}
		}

		return mav;
	}           

  4.調用ServletInvocableHandlerMethod#invokeHandlerMethod方法

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        // 封裝請求
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
        // 資料綁定工廠  用于請求參數與controller的參數(實體類)類型和參數轉換
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		// 設定請求參數解析器,預設有27個
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		// 設定傳回值處理器,預設15個
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		invocableMethod.setDataBinderFactory(binderFactory);
		// 設定參數名稱解析器
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		// 擷取請求重定向前的參數  DispatcherServlet#doService方法中設定的
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		// 設定預設模型不允許在重定向場景中使用
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			LogFormatUtils.traceDebug(logger, traceOn -> {
				String formatted = LogFormatUtils.formatValue(result, !traceOn);
				return "Resume with async result [" + formatted + "]";
			});
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}
		// 調用目标方法進行業務邏輯處理,并對傳回值進行處理
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}
		// 傳回結果  invocableMethod.invokeAndHandle方法中标記了請求處理完成
		return getModelAndView(mavContainer, modelFactory, webRequest);
	}           

  5.ServletInvocableHandlerMethod#invokeAndHandle方法,該方法完成業務方法的調用,以及對業務方法傳回結果的處理

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		// 調用業務方法,并傳回執行結果
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
		setResponseStatus(webRequest);

		if (returnValue == null) {
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
				disableContentCachingIfNecessary(webRequest);
				mavContainer.setRequestHandled(true);
				return;
			}
		}
		else if (StringUtils.hasText(getResponseStatusReason())) {
			mavContainer.setRequestHandled(true);
			return;
		}

		mavContainer.setRequestHandled(false);
		Assert.state(this.returnValueHandlers != null, "No return value handlers");
		try {
			// 對業務方法的傳回結果進行處理
			this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
		}
		catch (Exception ex) {
			if (logger.isTraceEnabled()) {
				logger.trace(formatErrorForReturnValue(returnValue), ex);
			}
			throw ex;
		}
	}           

  6.調用ServletInvocableHandlerMethod#invokeAndHandle方法,該方法中會調用invokeForRequest方法,此方法中調用doInvoke方法,最終完成業務方法調用,并将業務方法的執行結果傳回

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		// 參數解析
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		// 調用業務方法
		return doInvoke(args);
	}           

  7.調用HandlerMethodReturnValueHandlerComposite#handleReturnValue對傳回結果進行處理,該方法中會調用RequestResponseBodyMethodProcessor#handleReturnValue方法處理結果(controller層方法标注有@ResponseBody注解的方法)

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
		// 設定标記,說明請求已經處理完成
		mavContainer.setRequestHandled(true);
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		if (returnValue instanceof ProblemDetail detail) {
			outputMessage.setStatusCode(HttpStatusCode.valueOf(detail.getStatus()));
			if (detail.getInstance() == null) {
				URI path = URI.create(inputMessage.getServletRequest().getRequestURI());
				detail.setInstance(path);
			}
		}

		// Try even with null return value. ResponseBodyAdvice could get involved.
		// 此處完成業務方法傳回值->響應體内容的轉換,也是實作自已傳回格式的輸出位置
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}           

  8.上述方法執行完成後,傳回到RequestMappingHandlerAdapter#handleInternal方法

.....
	else {
			// No synchronization on session demanded at all...
			// 非同步阻塞請求,實際調用此處
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}

		if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
			if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
				applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
			}
			else {
				// 對響應内容進行預處理,如對響應内容設定緩存時間等,緩存有效期内,浏覽器不會在發起請求
				prepareResponse(response);
			}
		}

	return mav;           

  8.傳回到DispatcherServlet#doServlet方法,執行完ha.handle調用後。首先,執行請求處理鍊中的postHandle方法,然後執行processDispatchResult檢測是否發生異常,沒有異常,則反向執行請求鍊中的afterCompletion方法(預設方法空,沒有實際的邏輯),到此doDispatch方法分析完成。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            ......
				// Actually invoke the handler.
                // 執行請求處理,調用AbstractHandlerMethodAdapter類中的handle方法
				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 ServletException("Handler dispatch failed: " + err, err);
			}
			// 處理modelAndView,如果出現異常,則處理異常,抛出;否則,反向執行請求處理鍊中的afterCompletion方法
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new ServletException("Handler processing failed: " + err, 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);
				}
			}
		}
	}           

繼續閱讀