SpringMVC中servlet處理http請求原始碼解析
阿新 • • 發佈:2019-01-02
Spring MVC的核心控制器為Servlet,所有訪問服務端的請求都將由servlet攔截接受,並進行相應處理最終進行返回。下面我們來看看它究竟是怎麼做的。
SpringMVC中的Servlet一共有三個層次,分別是HttpServletBean、FrameworkServlet和 DispatcherServlet。
HttpServletBean直接繼承自java的HttpServlet,其作用是將Servlet中配置的引數設定到相應的屬性;FrameworkServlet初始化WebApplicationContext,而DispatchServlet初始化SpringMVC九大元件。可以具體看程式碼:
// 初始化servlet九大元件 protected void initStrategies(ApplicationContext context) { // 初始化檔案上傳元件,將普通的request包裝成MultipartHttpServletRequest,後者可以直接通過getFile獲取檔案 initMultipartResolver(context); // 初始化local解析器(EN-US;ZH-CN) initLocaleResolver(context); // 初始化主題解析器 initThemeResolver(context); // 初始化HandlerMapping initHandlerMappings(context); // 初始化HandlerAdapters initHandlerAdapters(context); // 初始化全域性異常處理類 initHandlerExceptionResolvers(context); // 初始化RequestToViewNameTranslator,從request中獲取ViewName initRequestToViewNameTranslator(context); // 初始化檢視解析器,將String型別檢視名和local解析為View型別檢視 initViewResolvers(context); // 初始化FlashMap(FlashMap用於在redirect中傳遞引數) initFlashMapManager(context); }
而http請求被servlet攔截後都將呼叫doService方法以排程到具體的handlerMapping中進行處理:
/** * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch} * for the actual dispatching. */ @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { if (logger.isDebugEnabled()) { // 獲取請求來源uri String requestUri = urlPathHelper.getRequestUri(request); String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : ""; logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + requestUri + "]"); } // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. // 將request請求中的各個屬性引數放入一個快照Map中 Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { logger.debug("Taking snapshot of request attributes before include"); attributesSnapshot = new HashMap<String, Object>(); // 獲取該請求所有引數 Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); // cleanupAfterInclude預設為true,若為servlet引數則放入Map中 if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // Make framework objects available to handlers and view objects. // 將mvc的webApplicationContext、主題解析器、主題、local解析器放入請求引數中 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()); // 獲取之前請求中儲存的所有FlashMap,然後刪除過期的FlashMap,並選取一個適合當前請求的FlashMap返回 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); // 若找到合適的FlashMap,將其轉換成一個不可變Map(不支援任何修改操作 put,remove,clear都將報錯),放入請求引數中 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 { // 將請求排程至handlerMapping處理 doDispatch(request, response); } finally { if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return; } // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } }
dodispatch方法將真正將請求定位至處理方法中,並在其中加入攔截器的處理、異常處理、返回檢視的構建:
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//WebAsyncManager主要用來管理非同步請求的處理,當業務邏輯複雜(或者其他原因),為了避免請求執行緒阻塞,需要委託給另一個執行緒時使用
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 判斷請求是否為檔案上傳型別,若是則返回MultipartRequest,若否則返回原request
processedRequest = checkMultipart(request);
// 判斷是否為MultipartRequest標識,若上一句 返回不是原request則證明是MultipartRequest
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
// handlerMappings中包含了所有註冊在IOC容器中的Handler
// ①遍歷handlerMappings列表,找到與request中請求名稱相同的handler,通過ApplicationContext.getBean方法獲取handlerBean
// ②通過獲取到的handlerBean再獲取可將handler攔截的攔截器鏈
mappedHandler = getHandler(processedRequest);
// 若未找到對應的handler則丟擲異常或返回http-404
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 通過handler找到對應的HandlerAdapter
// HandlerAdapter為處理器介面卡,作用為根據請求去定位請求的具體處理方法是哪個
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 若此http請求為get方法,則獲取瀏覽器端上送的最後修改時間,若最後修改時間與伺服器端一致,則說明無變化
// 無變化則方法直接返回,瀏覽器可繼續用上次get方法獲取到的資料快取即可
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 此處依次遍歷執行攔截器鏈的preHandler方法,若其中有一個return false或者丟擲異常則執行攔截器鏈的afterCompletion方法,最後return此次請求
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 通過handlerAdapter定位到具體的處理方法處理請求
try {
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
// 獲取ModelAndView
applyDefaultViewName(request, mv);
// 此處反序遍歷執行攔截器鏈的postHandler方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 處理請求的返回資訊:若有異常則返回異常檢視;若返回正常則呼叫render方法返回正常檢視
// 並反序執行攔截器鏈的afterCompletion方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 若丟擲異常也需反序執行攔截器鏈的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
// 若丟擲Error級異常也需反序執行攔截器鏈的afterCompletion方法
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
// 若為檔案上傳請求,最後還需清理掉上傳檔案的快取資訊
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}