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);
}
}
}
業務流程圖解析如下——