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