HttpServletBean主要參與建立過程,並無涉及請求的處理。ios
FrameworkServlet重寫了service,doGet,doPost,doPut,doDelete,doOptions,doTrace方法(除了doHead的全部處理請求的方法),在service方法中增長了對PATCH類型請求的處理,其餘類型交給父類進行處理。doGet,doPost,doPut,doDelete都是本身處理,全部須要本身處理的請求都交給了processRequest方法進行統一處理。web
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); } }
@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); }
@Override protected final void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
@Override protected final void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
processRequest方法是FrameworkServlet中最核心的方法spring
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; //獲取LocaleContextHolder中原來保存的LocaleContext LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); // 獲取當前請求的LocaleContext LocaleContext localeContext = buildLocaleContext(request); //獲取RequestContextHolder中原來保存的RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //獲取當前請求的RequestAttributes ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); //異步處理管理器設置攔截器,異步請求後面介紹 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //將當前請求的LocaleContext,ServletRequestAttributes設置到LocaleContextHolder和RequestContextHolder initContextHolders(request, localeContext, requestAttributes); try { // 實際處理入口,模板方法在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 { // 恢復原來的LocaleContext,ServletRequestAttributes resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { boolean isRequestDispatch = request.getDispatcherType().equals(DispatcherType.REQUEST); String dispatchType = request.getDispatcherType().name(); if (failureCause != null) { if (!isRequestDispatch) { logger.debug("Unresolved failure from \"" + dispatchType + "\" dispatch: " + failureCause); } else if (logger.isTraceEnabled()) { logger.trace("Failed to complete request", failureCause); } else { logger.debug("Failed to complete request: " + failureCause); } } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Exiting but response remains open for further handling"); } else { int status = response.getStatus(); if (!isRequestDispatch) { logger.debug("Exiting from \"" + dispatchType + "\" dispatch (status " + status + ")"); } else { HttpStatus httpStatus = HttpStatus.resolve(status); logger.debug("Completed " + (httpStatus != null ? httpStatus : status)); } } } } //處理完後發佈ServletRequestHandledEvent消息 publishRequestHandledEvent(request, response, startTime, failureCause); } }
processRequest本身主要作了兩件事情:session
一、對LocaleContext和RequestAttributes的設置和恢復,LocaleContext裏面存放着本地化信息,好比zh-cn等。RequestAttributes是spring的一個藉口,經過它能夠get/set/removeAttrbute,根據scope參數判斷操做是request仍是session。具體實現是ServletRequestAttributes,看看它的數據結構和方法數據結構
private final HttpServletRequest request; @Nullable private HttpServletResponse response; @Nullable private volatile HttpSession session; private final Map<String, Object> sessionAttributesToUpdate = new ConcurrentHashMap<>(1);
@Override public Object getAttribute(String name, int scope) { if (scope == SCOPE_REQUEST) { if (!isRequestActive()) { throw new IllegalStateException( "Cannot ask for request attribute - request is not active anymore!"); } return this.request.getAttribute(name); } else { HttpSession session = getSession(false); if (session != null) { try { Object value = session.getAttribute(name); if (value != null) { this.sessionAttributesToUpdate.put(name, value); } return value; } catch (IllegalStateException ex) { // Session invalidated - shouldn't usually happen. } } return null; } } @Override public void setAttribute(String name, Object value, int scope) { if (scope == SCOPE_REQUEST) { if (!isRequestActive()) { throw new IllegalStateException( "Cannot set request attribute - request is not active anymore!"); } this.request.setAttribute(name, value); } else { HttpSession session = obtainSession(); this.sessionAttributesToUpdate.remove(name); session.setAttribute(name, value); } } @Override public void removeAttribute(String name, int scope) { if (scope == SCOPE_REQUEST) { if (isRequestActive()) { this.request.removeAttribute(name); removeRequestDestructionCallback(name); } } else { HttpSession session = getSession(false); if (session != null) { this.sessionAttributesToUpdate.remove(name); try { session.removeAttribute(name); // Remove any registered destruction callback as well. session.removeAttribute(DESTRUCTION_CALLBACK_NAME_PREFIX + name); } catch (IllegalStateException ex) { // Session invalidated - shouldn't usually happen. } } } }
2.處理完後發佈ServletRequestHandledEvent消息app
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response, long startTime, @Nullable Throwable failureCause) { //publishEvents能夠在配置Servlet時設置,默認爲true if (this.publishEvents && this.webApplicationContext != null) { // 不管請求是否執行成功都會發布消息 long processingTime = System.currentTimeMillis() - startTime; this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause, response.getStatus())); } }
寫一個簡單的日誌請求監聽器,只要繼承ApplicationListener監聽ServletRequestHandledEvent事件就好了。異步
@Component public class ServletRequestHandleEventListener implements ApplicationListener<ServletRequestHandledEvent> { private static Logger logger = Logger.getLogger("request process log"); public void onApplicationEvent(ServletRequestHandledEvent event){ logger.info(event.getDescription()); } }
打印日誌以下async
FrameworkServlet的一個處理任務到這就結束了,接下來應該是DispatcherServlet的表演時刻ide
DispatcherServlet是SpringMVC最核心的處理類,整個處理過程的頂層設計都在這個裏面,因此咱們要把這個類完全弄明白了。經過以前的分析咱們知道DispatcherServlet的入口是doService()工具
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request);//日誌 Map<String, Object> attributesSnapshot = null;//快照備份記錄用的 if (WebUtils.isIncludeRequest(request)) {// 當include請求是對request的Attribute作快照備份 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)); } } } // 對request設置一些屬性 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 { 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); } } } }
doService主要是對request設置了一些屬性,核心入口是doDispatch方法,doDispatch方法也很是的簡潔,最主要的任務是:
1根據request找到Handler;
2.根據Handler找到對應的HandlerAdapter;
3.用HandlerAdapter處理Handler,
4調用processDispatchResult方法處理上面處理後的結果
Handler:也就是處理器,它直接對應着Controller中@RequestMapping的全部方法均可以當作一個Handler,只要能夠實際處理請求的就能夠是Handler
HandlerMappering:用來查找Handler的,在SpringMVC中會有不少處理請求,每一個請求都須要一個Handler來處理,具體接收到一個請求後倒地使用哪一個Handler來處理呢,這就是HandlerMapping要作的事情。
HandlerAdapter:它是一個是配置用來調用handler處理事情,就是Handler用來幹活的工具
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;//封裝Model和View的容器 Exception dispatchException = null; try { //檢測是否上傳請求 processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // 根據request獲取Handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根據Handler找到相應的Adapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 處理get,head請求的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; } } // 執行相應Interceptor的preHandle if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 實際上調用處理程序,HandlerAdapter使用Handler處理請求 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //若是須要異步處理直接返回 if (asyncManager.isConcurrentHandlingStarted()) { return; } //當view爲空時(好比返回值爲void),根據request設置默認的view applyDefaultViewName(processedRequest, mv); //執行相應Interceptor的postHandler 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); } //處理返回結果。包括異常處理,渲染頁面,發出完成通知觸發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 { // 刪除上傳請求的資源 if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
一、首先檢查是否是上傳請求,若是是設置上傳的request,並設置標誌位爲true。
二、經過getHandler獲取Handler處理器鏈
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
三、接下來處理get,head請求的Last-Modified後一次調用相應Interceptor的preHandle
四、接下來最關鍵的就是讓HandlerAdapter使用Handler處理請求返回ModelAndView
五、繼續調用processDispatchResult方法處理返回的結果
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false; // 若是請求過程當中有異常則處理異常 if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // 渲染頁面 if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned."); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // 異步請求直接返回 return; } // 發出請求處理完成的通知,觸發Interceptor的afterCompletion if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } }
六、最後就是觸發Interceptor的afterCompletion方法了。
doDispatch方法就分析到這了,能夠看出它的頂層設計很簡潔,而後具體的處理交給不一樣的組件具體實現的。doDispatch處理流程圖以下(圖來源SpringMvc源碼分析與實戰)
總結:
三個Servlet的處理過程大體功能以下:
1.HttpServletBean:沒有參與實際請求的處理
2.FrameworkServlet:將不一樣類型的請求合併到processRequest方法統一處理,processRequest方法中作了三件事:
(1)調用doServeice模板方法具體處理請求
(2)將當前請求的LocalContext和ServletRequestAttributes設置到相應的Holder,並在請求處理完成後恢復。
(3)請求處理完成後發佈消息
3.DispatcherServlet:doService方法給request設置了一些屬性並將請求交給doDispatch方法處理,它使用了相應的組件完成處理請求,另外HanderMapping,Handler和HandlerAdapter這三個概念的含義以及他們之間的關係也很是重要