01 SpringMVC簡介
SpringMVC是一種基于Spring實作了Web MVC設計模式的請求驅動類型的輕量級Web架構,使用了MVC架構模式的思想,将web層進行職責解耦,并管理應用所需對象的生命周期,為簡化日常開發,提供了很大便利。
SpringMVC提供的核心元件包括請求分發器(DispatchServlet)、請求處理映射器(HandlerMapping)、請求處理擴充卡(Handler Adapter)、視圖解析器(View Resolver) 、動作處理器(Controller)和模型視圖對象(ModelAndView),通過以上各大元件的協同工作來完成用戶端的請求響應工作,使得MVC架構的設計理念得以實作。
02 SpringMVC工作流程
我們先來看一下SpringMVC的流程圖
(1)用戶端向web伺服器(如tomcat)發送一個http請求,web伺服器對http請求進行解析,解析後的url位址如果比對到DispatchServlet的映射路徑(通過web.xml中的servlet-mapping配置),web容器就會将請求交給DispatchServlet處理
(2)DispatcherServlet接收到這個請求後,再對URL進行解析,得到請求資源辨別符(URI)。然後調用相應方法得到的HandlerMapping對象,再根據URI,調用這個對象的相應方法獲得Handler對象以及它對應的攔截器。(在這裡隻是獲得了Handler對象,并不會操作它,在SpringMVC中,是通過HandlerAdapter對Handler進行調用、控制的)
(3)DispatcherServlet根據得到的Handler對象,選擇一個合适的HandlerAdapter,建立其執行個體對象,執行攔截器中的preHandler()方法。
(4)在攔截器方法中,提取請求中的資料模型,填充Handler入參,是以所有準備工作都已做好,開始執行Handler(我們寫的controller代碼并不是能被直接執行,需要有剛才那些操作,才能轉變為Handler被執行)。
(5)Handler執行完畢後傳回一個ModelAndView對象給DispatcherServlet。
(6)這個ModleAndView隻是一個邏輯視圖,并不是真正的視圖,DispatcherServlet通過ViewResolver視圖解析器将邏輯視圖轉化為真正的視圖(通俗了解為将視圖名稱補全,如加上路徑字首,加上.jsp字尾,能指向實際的視圖)。
(7)DispatcherServlet通過Model将ModelAndView中得到的處資料解析後用于渲染視圖。将得到的最終視圖通過http響應傳回用戶端。
03 SpringMVC元件解析
Part.01 HandlerMapping
該元件在SpringMVC體系結構中有着舉足輕重的地位,充當着url和Controller之間映射關系配置的角色。主要有三部分組成:HandlerMapping映射注冊、根據url擷取對應的處理器、攔截器注冊。
在Spring MVC中,關于HandlerMapping的使用,主要包括兩個部分:注冊和查找。在HandlerMapping的實作中,持有一個handlerMap這樣一個HashMap<String, Object>,其中key是http請求的path資訊,value可以是一個字元串,或者是一個處理請求的HandlerExecutionChain,如果是String類型,則會将其視為Spring的bean名稱。在HandlerMapping對象的建立中,IoC容器執行了一個容器回調方法setApplicationContext,在這個方法中調用initApplicationContext方法進行初始化,各個子類可以根據需求的不同覆寫這個方法。關于handlerMap資訊的注冊就是在initApplicationContext方法中被執行的。
Part.02 HandlerAdapter
該元件在SpringMVC請求執行過程中包括兩個部分,HandlerAdapter的注冊和HandlerAdapter的執行。
(1)HandlerAdapter的注冊:
DispatcherServlte會根據配置檔案資訊注冊HandlerAdapter,如果在配置檔案中沒有配置,那麼DispatcherServlte會擷取HandlerAdapter的預設配置,如果是讀取預設配置的話,DispatcherServlte會讀取DispatcherServlte.properties檔案,該檔案中配置了三種HandlerAdapter:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter。DispatcherServlte會将這三個HandlerAdapter對象存儲到它的handlerAdapters這個集合屬性中,這樣就完成了HandlerAdapter的注冊。
(2)HandlerAdapter的執行:
DispatcherServlte會根據handlerMapping傳過來的controller與已經注冊好了的HandlerAdapter一一比對,看哪一種HandlerAdapter是支援該controller類型的,如果找到了其中一種HandlerAdapter是支援傳過來的controller類型,那麼該HandlerAdapter會調用自己的handle方法,handle方法運用java的反射機制執行controller的具體方法來獲得ModelAndView,例如SimpleControllerHandlerAdapter是支援實作了controller接口的控制器,如果自己寫的控制器實作了controller接口,那麼SimpleControllerHandlerAdapter就會去執行自己寫控制器中的具體方法來完成請求。
04 SpringMVC源碼簡單剖析
根據上邊的圖我們可以看到前端控制器在springMVC的系統中擁有了非常大的作用,那麼我們就以此來入手。
- 首先在它的關系圖中我們可以看出,DispatcherServlet類繼承FrameworkServlet類,而FrameworkServlet繼承了HttpServletBean類,再往上就是HttpServlet類與Servlet類了。
- 那麼我們就可以得出DispatchServlet是一個servlet類,既然是一個Servlet那麼就有一個核心方法:Service()方法,我們在DispatchServlet中找,發現沒有,那我們繼續去它的父類中找,于是發現了如下方法:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (HttpMethod.PATCH != httpMethod && httpMethod != null) {
super.service(request, response);
} else {
this.processRequest(request, response);
}
}
- 它首先判斷是不是PATCH請求,如果是的話執行父類的service()方法,如果不是執行 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 = this.buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor(null));
this.initContextHolders(request, localeContext, requestAttributes);
try {
this.doService(request, response);
} catch (IOException | ServletException var16) {
failureCause = var16;
throw var16;
} catch (Throwable var17) {
failureCause = var17;
throw new NestedServletException("Request processing failed", var17);
} finally {
this.resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (this.logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", (Throwable)failureCause);
} else if (asyncManager.isConcurrentHandlingStarted()) {
this.logger.debug("Leaving response open for concurrent processing");
} else {
this.logger.debug("Successfully completed request");
}
}
this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
}
}
- 這裡邊的代碼主要是執行一些控制器等等,可以不用了解太多,我們主要是了解其執行流程。在其中我們可以看到 this.doService(request, response);方法,來執行一些事務,那麼我們繼續追蹤:
protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
- 我們發現它是調用了子類:DispatcherServlet類中的方法,繼續追蹤:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (this.logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
label112:
while(true) {
String attrName;
do {
if (!attrNames.hasMoreElements()) {
break label112;
}
attrName = (String)attrNames.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.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 {
this.doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
- 我們看到它首先設定了一些request屬性,然後執行了一個this.doDispatch(request, response)方法,至此我們算是找到了它的核心執行代碼:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
//這個方法下面有單獨說明
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//這個方法下面有單獨說明
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//這個方法下面有單獨說明
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
//這個方法下面有單獨說明
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//這個方法下面有單獨說明
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
//這個方法下面有單獨說明
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//這個方法下面有單獨說明
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
- 這段代碼可以分成四個部分:
- ①查找Handler對應的HandlerMapping部分:
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
- 它首先判斷request請求是不是二進制請求,然後執行this.getHandler(processedRequest);方法。那麼我們來了解一下這個方法:
- ②this.getHandler(processedRequest)部分
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext()) {
HandlerMapping hm = (HandlerMapping)var2.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + this.getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
- 首先:我們發現它擷取了一個HandlerMapping的疊代器,然後查找每一個HandlerMapping。那麼它為什麼要先去查找HandlerMapping呢?那是因為我們在配置Controller的時候有兩種方式:xml和注解的方式,是以它先去找到HandlerMapping,然後根據HandlerMapping去找Handler,就是hm.getHandler(request);這個方法來查找Handler。
- ③查找HandlerAdapter部分:
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
④在拿到HandlerMapping之後,開始查找HandlerAdapter,就是ha.getLastModified(request, mappedHandler.getHandler());來查找。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter ha = (HandlerAdapter)var2.next();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
它的查找方式與之前查找HandlerMapping類似。
- 攔截器執行部分:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}
以上一小段代碼就是擷取攔截器然後執行。
- 再接下來就是最重要的Hanler執行部分,它傳回的對象是mv,就是ModelAndView對象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//這個方法下面有說明
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
它首先在 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 執行了handler并傳回了一個視圖;然後通過 this.applyDefaultViewName(processedRequest, mv); 在我們沒有設定視圖名稱的時候傳回預設視圖名;然後通過 this.applyDefaultViewName(processedRequest, mv); 擷取到項目的攔截器,然後判斷攔截器是否存在,存在則對該請求進行測試(postHandler),如果有一個攔截器不成功,則請求失敗。最後來到視圖解析和渲染的執行方法:this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
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) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
if (mv != null && !mv.wasCleared()) {
//這個方法下面有說明
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isDebugEnabled()) {
this.logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + this.getServletName() + "': assuming HandlerAdapter completed request handling");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
它首先判斷一下是否異常,如果出現異常,判斷異常是否是ModelAndViewDefiningException的執行個體,如果是就直接調用getModelAndView方法傳回一個mv,否則調用processHanlderException方法初始化mv對象;如果沒有異常出現,那麼判斷mv是否為空,為空抛出異常,不為空則判斷mv,wasCleared()方法傳回的值是否為真;判斷完成以後,執行了 this.render(mv, request, response);方法。
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();
response.setLocale(locale);
String viewName = mv.getViewName();
View view;
if (viewName != null) {
view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
}
} else {
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
} catch (Exception var8) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var8);
}
throw var8;
}
}
它首先設定了視圖的編碼格式,然後通過 view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);進行了視圖解析:
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes);
}
Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);
this.prepareResponse(request, response);
this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
}
它首先通過 Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);擷取了傳入的model的map集合。然後通過 this.prepareResponse(request, response);設定響應頭的兩個參數,最後通過 this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);将mergeModel合到請求裡.
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
this.exposeModelAsRequestAttributes(model, request);
this.exposeHelpers(request);
String dispatcherPath = this.prepareForRendering(request, response);
RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + this.getUrl() + "]: Check that the corresponding file exists within your web application archive!");
} else {
if (this.useInclude(request, response)) {
response.setContentType(this.getContentType());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Including resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");
}
rd.include(request, response);
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Forwarding to resource [" + this.getUrl() + "] in InternalResourceView '" + this.getBeanName() + "'");
}
rd.forward(request, response);
}
}
}
它首先進行了 this.exposeModelAsRequestAttributes(model, request);操作:
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
model.forEach((modelName, modelValue) -> {
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + this.getBeanName() + "'");
}
} else {
request.removeAttribute(modelName);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Removed model object '" + modelName + "' from request in view with name '" + this.getBeanName() + "'");
}
}
});
}
由此可見,model中的資料,最終是放在request域中的。
然後他們進行了RequestDispatcher 對象配置,就是選擇它要進行的操作了,是使用include還是forward進行跳轉。至此,視圖的解析與渲染工作都完成了,并且也發送到了前端頁面,完成了一個請求的整個流程。
05 總結
SpringMVC的總體執行流程的源碼解析就為以上描述,我們發現SpringMVC最主要的控制器類DispatcherServlet,它在項目的web.xml中配置攔截位址。當我們發送請求時就可以進入到以上代碼中,進而完成整個請求處理。