一、Spring MVC
Spring MVC 基於模型-檢視-控制器(Model-View-Controller, MVC)模式實現,並且很好的實現了軟體設計中的開閉原則(即對擴充套件開放,對修改關閉),當因為業務需要對Spring MVC做些定製化處理時,就會發現Spring MVC對功能擴充套件是極其友好的、在後續的原始碼解析系列文章中我們會陸續看到Spring MVC在處理請求的各個步驟中都可以定製所需要的功能。
二、Spring MVC 整體框架
整個系列的文章都會圍繞這張圖進行,會對每一個步驟進行詳細講解。
首先我們先來看一下 DispatcherServlet diagram
可以看到藍色的繼承關係到 Servlet 相信大家在學習MVC
框架之前對 HttpServlet
非常的熟悉,目前還有一些老專案在使用原生的 java servlet
進行專案開發。我們看一下 servlet
介面最重要的方法簽名:
/**
* Called by the servlet container to allow the servlet to respond to
* a request.
*
* <p>This method is only called after the servlet's <code>init()</code>
* method has completed successfully.
*
* <p> The status code of the response always should be set for a servlet
* that throws or sends an error.
*
*
* <p>Servlets typically run inside multithreaded servlet containers
* that can handle multiple requests concurrently. Developers must
* be aware to synchronize access to any shared resources such as files,
* network connections, and as well as the servlet's class and instance
* variables.
* More information on multithreaded programming in Java is available in
* <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
* the Java tutorial on multi-threaded programming</a>.
*
*
* @param req the <code>ServletRequest</code> object that contains
* the client's request
*
* @param res the <code>ServletResponse</code> object that contains
* the servlet's response
*
* @exception ServletException if an exception occurs that interferes
* with the servlet's normal operation
*
* @exception IOException if an input or output exception occurs
*
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
複製程式碼
這個方法是servlet
處理web
請求的入口。Spring MVC
在DispatchSerlvet
還分裝了一層FrameWorkServlet
用於統一處理所有不同方法型別(GET
、POST
等)的請求
三、各元件的基本介紹。
我們從一個Http
請求的角度,來大致瞭解Spring MVC是處理請求的大致流程(例如,到controller
方法加@ResponseBody
時就不會有檢視解析這一步)。
1、web container 接收到一個請求,容器呼叫已經註冊好的DispatcherServlet
,後者通過Request
物件到RequestMapping
獲取對應的 handler
(即controller
層實際呼叫的方法)。
2、執行interceptor
的preHandler()
方法。
3、執行第一步獲取的Controller
方法,並返回ModelAndView
。
4、執行interceptor
的postHandler()
方法。
5、檢視渲染,執行ViewResolve.resolveViewName()方法回去檢視檔案。
/**
* 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 asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//判斷請求是否是 multipart post (常見的有 post 表單提交的資料)
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
//通過request獲取handler,包括 intercepter 資訊
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//獲取 handler 介面卡
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()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//呼叫 intercepter.perHandler()方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
//呼叫controller方法發揮 ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//當 ModelAndView 中不包含檢視時獲取預設檢視
applyDefaultViewName(processedRequest, mv);
//呼叫 intercepter.perHandler()方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//檢視渲染並將渲染後的檢視檔案(html)或者 json 等寫入Response body 返回給瀏覽器
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
複製程式碼
總結
本文主要介紹了Spring MVC的一些概念以及請求執行的大致過程。後續的文章將繼續分析Spring MVC的各個元件,以及如何根據自己的專案定製相應的功能。