1. 程式人生 > >SpringMVC框架業務流程原始碼分析學習筆記【JAVA核心】

SpringMVC框架業務流程原始碼分析學習筆記【JAVA核心】

1.SpringMVC框架是什麼?遵循java web什麼規範?

SpringMVC是一個基於Spring生態圈之上封裝的一個處理web層請求的半封裝框架。

那什麼叫半封裝框架?

半封裝指的SpringMVC是基於servlet處理web層這一技術體系的擴充套件與延伸——

(1)基於servlet的體系,執行servlet規範,容器當中可以無縫整合

(2)基於spring生態圈的,那麼它使用spring資源非常容易

SpringMVC遵循sun公司制定的Java web規範——servlet規範。servlet規範定義了web層的請求處理規範,主要涉及以下幾個方法:

package javax.servlet;

import java.io.IOException;
/**
 * Defines methods that all servlets must implement.
 * /
public interface Servlet {
    void init(ServletConfig config) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

從繼承圖來看,也能明顯看出SpringMVC實現了servlet規範。GenericServlet與HttpServlet是Sun公司定義的,從HTTPServletBean開始為SpringMVC框架層。

2.何為SpringMVC上下文(環境支援)?

上下文就是Bean Container:bean的容器技術就算上下文,特別是bean的依賴注入 IOC。

啟動專案前需要啟動web容器——



監聽外部配置檔案有沒有載入進來,載入後成為Spring的根上下文——>實際上就是提供基礎底層的bean的元件

3.SpringMVC如何無縫的和web容器結合的?

springMVC實現了servlet,其本質上就是一個servlet服務

web容器是在web.xml當中掛servlet服務的

<servlet>
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

那web容器是如何知道按照什麼樣的規範解讀部署在其內部的servlet服務的?

答案還是servlet規範——負責解讀部署的專案的web容器,其內部早就植入了servlet規範,以Tomcat為例:


如上圖所示:在Apache-Tomcat中已經有了servlet規範
下圖是無縫結合的場景——《SpringMVC在Tomcat當中是如何工作?》


Tomcat接受了http請求,內部環境就是一個servlet環境,因為其內部依賴了servlet規範。執行的第一步就是init模組,初始化的時候會讀取web.xml中的檔案資訊。何以見得?我們看一下原始碼:


GenericServlet中的init方法讀取了配置資訊,會呼叫SpringMVC框架中子類HTTPServletBean中重寫的初始化方法init(Config),上圖中的read會讀取servletConfig。

4.SpringMVC框架處理http請求的業務流程

Tomcat收到http請求

4.1 第一步

呼叫Sun框架中的介面方法:javax.servlet.Servlet#service(ServletRequest req, ServletResponse res)方法

4.2 第二步

呼叫介面具體實現類方法——org.springframework.web.servlet.FrameworkServlet#service()方法

	/**
	 * Override the parent class implementation in order to intercept PATCH
	 * requests.
	 */
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		String method = request.getMethod();
		if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
			processRequest(request, response);
		}
		else {
			super.service(request, response);
		}
	}

註釋寫的很清楚,重寫該方法是為了攔截PATCH請求,這個PATCH請求是SpringMVC在Sun公司的基礎上增加的一個請求型別

public enum RequestMethod {

	GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE

}

4.3 第三步

除了PATCH請求的其它方法都呼叫了父類(Sun框架)javax.servlet.http.HttpServlet中的service方法


判定請求方法型別,然後派發請求給各個處理函式

特別對GET方法進行了IMS(If-Modified-Since)比較

 if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // 伺服器不支援IMS,直接開始處理邏輯
                doGet(req, resp);
            } else {  //伺服器支援IMS比較,那就先判斷
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // 服務端資源有更新
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {//服務端資源無更新
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } 

4.4 第四步

上面呼叫的各個處理函式org.springframework.web.servlet.FrameworkServlet都被重寫,最終處理方法統一於其方法——processRequest

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		try {
			doService(request, response);
		}
		catch (ServletException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}

			if (logger.isDebugEnabled()) {
				if (failureCause != null) {
					this.logger.debug("Could not complete request", failureCause);
				}
				else {
					if (asyncManager.isConcurrentHandlingStarted()) {
						logger.debug("Leaving response open for concurrent processing");
					}
					else {
						this.logger.debug("Successfully completed request");
					}
				}
			}

			publishRequestHandledEvent(request, startTime, failureCause);
		}
	}

4.5 第五步

呼叫org.springframework.web.servlet.FrameworkServlet#processRequest()中的doService()方法,該抽象方法被DispatchServlet實現,因此最終呼叫org.springframework.web.servlet.DispatchServlet#doService()(原始碼分析待續)

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (logger.isDebugEnabled()) {
			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.
		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();
				if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		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 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()) {
				return;
			}
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}

其中又呼叫doDispatch()方法,(原始碼分析待續)流程圖解析如下——

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 || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				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;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				try {
					// Actually invoke the handler.
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			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);
			}
		}
	}

業務流程圖解析如下——


5.手寫SpringMVC