1. 程式人生 > >SpringMVC中servlet處理http請求原始碼解析

SpringMVC中servlet處理http請求原始碼解析

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