Spring Web MVC是基於Servlet API構建的原始Web框架,從一開始就已包含在Spring框架中。傳統的模型層被拆分爲了業務層(Service)和數據訪問層(DAO,Data Access Object)。 在 Service 下能夠經過 Spring 的聲明式事務操做數據訪問層,而在業務層上還容許咱們訪問 NoSQL ,這樣就可以知足異軍突起的 NoSQL 的使用了,它能夠大大提升互聯網系統的性能。ios
M 表明 模型(Model),V 表明 視圖(View),C 表明 控制器(controller):web
模型封裝了應用程序數據,而且一般它們由 POJO 組成。spring
視圖主要用於呈現模型數據,而且一般它生成客戶端的瀏覽器能夠解釋的 HTML 輸出。設計模式
1.在Spring MVC中,控制器能夠處理全部HTTP方法的請求,這是RESTful Web服務的基礎。例如,能夠處理GET方法來執行讀取操做,POST方法來建立資源,PUT方法來更新資源以及DELETE方法來從服務器中刪除資源。從Spring 3.2開始,還能夠處理PATCH請求。瀏覽器
2.對於REST,數據的表示很是重要,這就是爲何Spring MVC容許您經過使用 @ResponseBody 批註和各類 HttpMessgeConverter 實現方式徹底繞過基於View的呈現 。經過使用此功能,您能夠直接向客戶端發送響應,例如,客戶端所需的資源以及客戶端所需的格式。
spring-mvc
3. Spring 4.0版本添加了專用註釋,@RestController使RESTful Web服務的開發更加容易。服務器
若是使用@RestController 而不是 @Controller註釋控制器類 ,則 Spring 會將消息對話應用於控制器中的全部處理程序方法。多線程
這意味着您不須要爲每一個方法添加 @ResponseBody 註釋。這也使您的代碼更加簡潔。 架構
4. REST Web服務和普通Web應用程序之間的主要區別之一是,REST傳遞資源標識URI自己中的數據,例如 /messages/101, 而Web應用程序一般使用查詢參數,例如 /messages?Id=101。mvc
若是您還記得的話,咱們使用 @RequestParam 獲取這些查詢參數的值,可是不用擔憂,Spring MVC還提供了一個@PathVariable批註,能夠從URL提取數據。它容許控制器處理對參數化URL的請求。
5. RESTful Web服務的另外一個關鍵方面是表示,表示同一資源能夠以不一樣的格式表示,例如JSON,XML,HTML等。值得慶幸的是,Spring提供了幾種視圖實現和視圖解析器,以將數據呈現爲JSON,XML,和HTML。
例如,ContentNegotiatingViewResolver 能夠查看請求的文件擴展名或「接受」標頭,以找出客戶端資源的正確表示形式。
6.與 @ResponseBody 用於將響應轉換爲客戶端想要的格式(經過使用 HttpMessageConverts)的註釋 相似 ,Spring MVC還提供 @RequestBody
註釋,該註釋使用 HttpMethodConverter 實現將入站HTTP數據轉換爲傳遞到控制器的處理程序方法中的Java對象。
7.在 Spring框架還提供了一個模板類,RestTemplate,這是相似的 JdbcTemplate,而且 JmsTemplate,它能夠消耗REST資源。您可使用此類測試RESTful Web服務或開發REST客戶端。
DispatchServlet 是 HTTP 請求處理程序/控制器的中央調度程序。Spring Web 模型-視圖-控制(MVC)框架是圍繞 DispatcherServlet 設計的。
下面是對應於 DispatcherServlet 傳入 HTTP 請求的事件序列:
收到一個 HTTP 請求後,DispatcherServlet 根據 HandlerMapping 來選擇而且調用適當的控制器
控制器接受請求(取決於使用@Controller和@RequestMapping註釋請求的URL),並基於使用的 GET 或 POST 方法來調用適當的 service 方法。Service 方法將設置基於定義的業務邏輯的模型數據,並返回視圖名稱到 DispatcherServlet 中。
DispatcherServlet 會從視圖解析器 ViewResolver 獲取幫助,爲請求檢取定義視圖。
上面所提到的全部組件,即 HandlerMapping、Controller 和 ViewResolver 是 WebApplicationContext 的一部分,而 WebApplicationContext 是帶有一些對 web 應用程序必要的額外特性的 ApplicationContext 的擴展。
DispatcherServlet在MVC子容器初始化時會初始化這9大組件:
MultipartResolver:用來處理上傳請求的。其處理方式就是將 request 包裝成 MultipartHttpServletRequest。而後就能夠用 MultipartHttpServletRequest 這個直接調用 getFile 獲取的文件了。
LocalResolver:做用是從 request 中解析出 local,關於local大多數狀況下都是用來作國際化處理的。
ThemeResolver:解析主題的,例如配一套properties文件供系統在不一樣的時候讀取切換。
HandlerMapping:其做用就是根據 request 找到相應的處理器 Handler 和 Intecepter 攔截器。
HandlerAdapter:其接口的 handle 方法就是使用 handler 來處理邏輯的。
HandlerExceptionResolver:Spring MVC 中的異常處理組件,根據異常設置 ModelAndView,而後再將處理結果交給 render
方法進行渲染。在渲染以前工做的,所以渲染過程當中發生異常,HandlerExceptionResolver 是不會處理的。
RequestToViewNameTranslator:將request請求轉換爲視圖名稱。
ViewResolver:做用是將 String 類型的邏輯視圖根據 local 解析爲 View 視圖的。
FlashMapManager:在redirect是進行參數傳遞須要用到。
1 /** 2 * This implementation calls {@link #initStrategies}. 3 */ 4 @Override 5 protected void onRefresh(ApplicationContext context) { 6 initStrategies(context); 7 }
1 /** 2 * Initialize the strategy objects that this servlet uses. 3 * <p>May be overridden in subclasses in order to initialize further strategy objects. 4 */ 5 protected void initStrategies(ApplicationContext context) { 6 initMultipartResolver(context); 7 initLocaleResolver(context); 8 initThemeResolver(context); 9 initHandlerMappings(context); 10 initHandlerAdapters(context); 11 initHandlerExceptionResolvers(context); 12 initRequestToViewNameTranslator(context); 13 initViewResolvers(context); 14 initFlashMapManager(context); 15 }
Http請求經過 HttpServlet 讀取請求的內容,生成一個 HttpServletRequest 和一個 HttpServletResponse。下面這段方法將客戶端請求分派到受保護的 service 方法。
1 @Override 2 public void service(ServletRequest req, ServletResponse res) 3 throws ServletException, IOException { 4 5 HttpServletRequest request; 6 HttpServletResponse response; 7 8 try { 9 request = (HttpServletRequest) req; 10 response = (HttpServletResponse) res; 11 } catch (ClassCastException e) { 12 throw new ServletException(lStrings.getString("http.non_http")); 13 } 14 service(request, response); 15 }
接着來到 FrameworkServlet 的 service 方法,如果 patch 類型的方法,則單獨拿出來進行處理。
1 /** 2 * Override the parent class implementation in order to intercept PATCH requests. 3 */ 4 @Override 5 protected void service(HttpServletRequest request, HttpServletResponse response) 6 throws ServletException, IOException { 7 8 HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); 9 if (httpMethod == HttpMethod.PATCH || httpMethod == null) { 10 processRequest(request, response); 11 } 12 else { 13 super.service(request, response); 14 } 15 }
最後回到 HttpServlet 的 service 方法中,這裏使用到的設計模式是模板方法。
1 protected void service(HttpServletRequest req, HttpServletResponse resp) 2 throws ServletException, IOException { 3 4 String method = req.getMethod(); 5 6 if (method.equals(METHOD_GET)) { 7 long lastModified = getLastModified(req); 8 if (lastModified == -1) { 9 // servlet doesn't support if-modified-since, no reason 10 // to go through further expensive logic 11 doGet(req, resp); 12 } else { 13 long ifModifiedSince; 14 try { 15 ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); 16 } catch (IllegalArgumentException iae) { 17 // Invalid date header - proceed as if none was set 18 ifModifiedSince = -1; 19 } 20 if (ifModifiedSince < (lastModified / 1000 * 1000)) { 21 // If the servlet mod time is later, call doGet() 22 // Round down to the nearest second for a proper compare 23 // A ifModifiedSince of -1 will always be less 24 maybeSetLastModified(resp, lastModified); 25 doGet(req, resp); 26 } else { 27 resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); 28 } 29 } 30 31 } else if (method.equals(METHOD_HEAD)) { 32 long lastModified = getLastModified(req); 33 maybeSetLastModified(resp, lastModified); 34 doHead(req, resp); 35 36 } else if (method.equals(METHOD_POST)) { 37 doPost(req, resp); 38 39 } else if (method.equals(METHOD_PUT)) { 40 doPut(req, resp); 41 42 } else if (method.equals(METHOD_DELETE)) { 43 doDelete(req, resp); 44 45 } else if (method.equals(METHOD_OPTIONS)) { 46 doOptions(req,resp); 47 48 } else if (method.equals(METHOD_TRACE)) { 49 doTrace(req,resp); 50 51 } else { 52 // 53 // Note that this means NO servlet supports whatever 54 // method was requested, anywhere on this server. 55 // 56 57 String errMsg = lStrings.getString("http.method_not_implemented"); 58 Object[] errArgs = new Object[1]; 59 errArgs[0] = method; 60 errMsg = MessageFormat.format(errMsg, errArgs); 61 62 resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); 63 } 64 }
而後在跳到 FrameworkServlet 的 doGet 方法中,doGet方法中用到 processRequest 方法,再看看上面對 patch 進行單獨處理,可見是把將不一樣類型的請求合併到了processRequest 這個方法中來處理。
1 @Override 2 protected final void doGet(HttpServletRequest request, HttpServletResponse response) 3 throws ServletException, IOException { 4 5 processRequest(request, response); 6 }
processRequest 方法中作了三件事:
調用了 doService 模板方法具體處理請求。
將當前請求的 LocaleContext 和 ServletRequestAttributes 在處理請求前設置到了 LocaleContextHolder 和 RequestContextHolder,並在請求處理完成後恢復
請求處理完後發佈了 ServletRequestHandledEvent 消息。
1 protected final void processRequest(HttpServletRequest request, HttpServletResponse response) 2 throws ServletException, IOException { 3 4 long startTime = System.currentTimeMillis(); 5 Throwable failureCause = null; 6 7 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); 8 LocaleContext localeContext = buildLocaleContext(request); 9 10 RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); 11 ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); 12 13 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 14 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); 15 16 initContextHolders(request, localeContext, requestAttributes); 17 18 try { 19 doService(request, response); 20 } 21 catch (ServletException | IOException ex) { 22 failureCause = ex; 23 throw ex; 24 } 25 catch (Throwable ex) { 26 failureCause = ex; 27 throw new NestedServletException("Request processing failed", ex); 28 } 29 30 finally { 31 resetContextHolders(request, previousLocaleContext, previousAttributes); 32 if (requestAttributes != null) { 33 requestAttributes.requestCompleted(); 34 } 35 logResult(request, response, failureCause, asyncManager); 36 publishRequestHandledEvent(request, response, startTime, failureCause); 37 } 38 }
上面這段代碼調用了 doService 的模板方法,接着走到了 DispatcherServlet 類中的 doService方法。
1 @Override 2 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 3 logRequest(request); 4 5 // Keep a snapshot of the request attributes in case of an include, 6 // to be able to restore the original attributes after the include. 7 Map<String, Object> attributesSnapshot = null; 8 if (WebUtils.isIncludeRequest(request)) { 9 attributesSnapshot = new HashMap<>(); 10 Enumeration<?> attrNames = request.getAttributeNames(); 11 while (attrNames.hasMoreElements()) { 12 String attrName = (String) attrNames.nextElement(); 13 if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { 14 attributesSnapshot.put(attrName, request.getAttribute(attrName)); 15 } 16 } 17 } 18 19 // Make framework objects available to handlers and view objects. 20 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); 21 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); 22 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); 23 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 24 25 if (this.flashMapManager != null) { 26 FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); 27 if (inputFlashMap != null) { 28 request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); 29 } 30 request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); 31 request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); 32 } 33 34 try { 35 doDispatch(request, response); 36 } 37 finally { 38 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 39 // Restore the original attribute snapshot, in case of an include. 40 if (attributesSnapshot != null) { 41 restoreAttributesAfterInclude(request, attributesSnapshot); 42 } 43 } 44 } 45 }
而後進行請求的分發 DispatcherServlet#doDispatch
1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 HttpServletRequest processedRequest = request; 3 HandlerExecutionChain mappedHandler = null; 4 boolean multipartRequestParsed = false; 5 6 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 7 8 try { 9 ModelAndView mv = null; 10 Exception dispatchException = null; 11 12 try { 13 processedRequest = checkMultipart(request); 14 multipartRequestParsed = (processedRequest != request); 15 16 // 肯定當前請求的handler 17 mappedHandler = getHandler(processedRequest); 18 if (mappedHandler == null) { 19 noHandlerFound(processedRequest, response); // 沒有找到則執行此方法 20 return; 21 } 22 23 // 肯定當前請求的處理程序適配器 24 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 25 26 // Process last-modified header, if supported by the handler. 27 String method = request.getMethod(); 28 boolean isGet = "GET".equals(method); 29 if (isGet || "HEAD".equals(method)) { 30 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 31 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 32 return; 33 } 34 } 35 36 if (!mappedHandler.applyPreHandle(processedRequest, response)) { 37 return; 38 } 39 40 // 調用對應handler方法 41 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 42 43 if (asyncManager.isConcurrentHandlingStarted()) { 44 return; 45 } 46 47 applyDefaultViewName(processedRequest, mv); 48 mappedHandler.applyPostHandle(processedRequest, response, mv); 49 } 50 catch (Exception ex) { 51 dispatchException = ex; 52 } 53 catch (Throwable err) { 54 // As of 4.3, we're processing Errors thrown from handler methods as well, 55 // making them available for @ExceptionHandler methods and other scenarios. 56 dispatchException = new NestedServletException("Handler dispatch failed", err); 57 } 58 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 59 } 60 catch (Exception ex) { 61 triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 62 } 63 catch (Throwable err) { 64 triggerAfterCompletion(processedRequest, response, mappedHandler, 65 new NestedServletException("Handler processing failed", err)); 66 } 67 finally { 68 if (asyncManager.isConcurrentHandlingStarted()) { 69 // Instead of postHandle and afterCompletion 70 if (mappedHandler != null) { 71 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 72 } 73 } 74 else { 75 // Clean up any resources used by a multipart request. 76 if (multipartRequestParsed) { 77 cleanupMultipart(processedRequest); 78 } 79 } 80 } 81 }
看看 getHandler 方法,遍歷全部配置的 HandlerMapping 類,能夠看出是支持用戶配置多個 HandlerMapping 類的,
1 @Nullable 2 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 3 if (this.handlerMappings != null) { 4 for (HandlerMapping mapping : this.handlerMappings) { 5 HandlerExecutionChain handler = mapping.getHandler(request); 6 if (handler != null) { 7 return handler; 8 } 9 } 10 } 11 return null; 12 }
若是沒有找到對應的 HandlerExecutionChain 對象,則會執行 noHandlerFound 方法返回異常。
1 protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 if (pageNotFoundLogger.isWarnEnabled()) { 3 pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request)); 4 } 5 if (this.throwExceptionIfNoHandlerFound) { 6 throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), 7 new ServletServerHttpRequest(request).getHeaders()); 8 } 9 else { 10 response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404錯誤 11 } 12 }
返回到 DispatcherServlet#doDispatch,找到了對應的 handler 後,就去尋找對應的 HandlerAdapter。與 HandlerMapping 相似,查找可以處理具體 Handler 的 HandlerAdapter 時一樣會遍歷全部配置了的 HandlerAdapter,沒有找到對應的 adapter 則會拋出異常。
1 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 2 if (this.handlerAdapters != null) { 3 for (HandlerAdapter adapter : this.handlerAdapters) { 4 if (adapter.supports(handler)) { 5 return adapter; 6 } 7 } 8 } 9 throw new ServletException("No adapter for handler [" + handler + 10 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); 11 }
前沒有什麼問題發生,則按照順序依次調用 HandlerInterceptor的 preHandle 方法,但當任一 HandlerInterceptor 的 preHandle方法返回了 false 就再也不繼續執行其餘HandlerInterceptor 的 preHandle方法,而是直接跳轉執行 triggerAfterCompletion 方法。
1 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { 2 HandlerInterceptor[] interceptors = getInterceptors(); 3 if (!ObjectUtils.isEmpty(interceptors)) { 4 for (int i = 0; i < interceptors.length; i++) { 5 HandlerInterceptor interceptor = interceptors[i]; 6 if (!interceptor.preHandle(request, response, this.handler)) { 7 triggerAfterCompletion(request, response, null); 8 return false; 9 } 10 this.interceptorIndex = i; 11 } 12 } 13 return true; 14 }
而後執行 mapperHandler 的 applyPostHandle 方法,應用註冊攔截器的 postHandle 方法。
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } }
最後執行 processDispatchResult 方法
1 /** 2 * Handle the result of handler selection and handler invocation, which is 3 * either a ModelAndView or an Exception to be resolved to a ModelAndView. 4 */ 5 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 6 HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { 7 8 boolean errorView = false; 9 //判斷 HandlerMapping、HandlerAdapter 處理時的異常是否有異常,有異常則進行異常處理 10 if (exception != null) { 11 //上述兩個組件處理時的異常不爲空 12 //若是爲 ModelAndViewDefiningException 異常,則獲取一個異常視圖 13 if (exception instanceof ModelAndViewDefiningException) { 14 logger.debug("ModelAndViewDefiningException encountered", exception); 15 mv = ((ModelAndViewDefiningException) exception).getModelAndView(); 16 } 17 //若是不爲 ModelAndViewDefiningException 異常,進行異常視圖的獲取 18 else { 19 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 20 mv = processHandlerException(request, response, handler, exception); 21 errorView = (mv != null); 22 } 23 } 24 25 // Did the handler return a view to render? 26 //判斷 mv 是否爲空,不論是正常的 ModelAndView 仍是異常的 ModelAndView,只要存在 mv 就進行視圖渲染 27 if (mv != null && !mv.wasCleared()) { 28 render(mv, request, response); // 交給rander渲染 29 if (errorView) { 30 WebUtils.clearErrorRequestAttributes(request); 31 } 32 } 33 //不然記錄無視圖 34 else { 35 if (logger.isDebugEnabled()) { 36 logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + 37 "': assuming HandlerAdapter completed request handling"); 38 } 39 } 40 41 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 42 // Concurrent handling started during a forward 43 return; 44 } 45 //執行相關HandlerInterceptor的afterCompletion 方法 46 if (mappedHandler != null) { 47 mappedHandler.triggerAfterCompletion(request, response, null); 48 } 49 }
到這裏以後則按着棧幀一步步彈出,DispatcherServlet 處理完 Http 請求的流程。