【SpringMVC框架】前端控制器原始碼分析
阿新 • • 發佈:2018-12-31
前端控制器原始碼分析
雖然前面講了一些springmvc的入門程式和配置檔案中對映器和介面卡的配置,但是我們作為程式設計人員,瞭解框架的部分原始碼還是有必要的,比如前端控制器,它是如何通過Servlet的web.xml配置檔案實現攔截並跳轉至DispatcherServlet的呢?下面我們詳細探討
眾多周知我們的入門程式的web.xml是這麼配置的
還記不記得springmvc的執行過程:
通過前端控制器原始碼分析springmvc的執行過程。
我們點開org.springframework.web.servlet.DispatcherServlet看看,裡面有一個doDiapatch的方法:
我們來分析這個方法
第一步:前端控制器接收請求
.action型別的URL通過過濾器進入DispatcherServlet類,呼叫其doDiapatch()方法
第二步:前端控制器呼叫處理器對映器查詢 Handler
在doDiapatch()方法中呼叫了DispatcherServlet類的getHandler方法
說明對映器根據request當中的URL,找到了Handler,最終返回一個執行器的鏈(HandlerExecutionChain)。這個鏈裡面有Handler。
第三步:呼叫處理器介面卡執行Handler,得到執行結果ModelAndView
第四步:檢視渲染,將model資料填充到request域。
檢視解析,得到view:
在doDiapatch()方法中有這一句
其中processDispatchResult方法
其中render(mv, request, response);方法中有
渲染方法:
呼叫view的渲染方法,將model資料填充到request域
大致瞭解了原始碼,便於更好的理解框架。
雖然前面講了一些springmvc的入門程式和配置檔案中對映器和介面卡的配置,但是我們作為程式設計人員,瞭解框架的部分原始碼還是有必要的,比如前端控制器,它是如何通過Servlet的web.xml配置檔案實現攔截並跳轉至DispatcherServlet的呢?下面我們詳細探討
眾多周知我們的入門程式的web.xml是這麼配置的
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- SpringMvc前端控制器 --> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- contextConfigLocation配置springmvc載入的配置檔案(配置處理器對映器,介面卡等) 如果不配置contextConfigLoaction,預設載入的是/WEB-INF/servlet名稱-servlet.xml(springmvc-servlet.xml) --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- 第一種:*.action。訪問以.action結尾由DispatcherServlet進行解析; 第二種:/,所有訪問的地址都由DispatcherServlet進行解析,對於靜態檔案的解析, 我們要配置不讓DispatcherServlet進行解析。使用此種方法可以實現RESTful風格的url; 第三種:/*,這樣配置不對,使用這種配置,最終要轉發到一個jsp頁面時,仍然會由 DispatcherServlet進行解析jsp地址,它不能根據jsp頁面找到Handler,會報錯--> <url-pattern>*.action</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
還記不記得springmvc的執行過程:
圖-1.c.springmvc框架
通過前端控制器原始碼分析springmvc的執行過程。
我們點開org.springframework.web.servlet.DispatcherServlet看看,裡面有一個doDiapatch的方法:
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, false); 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); } } }
我們來分析這個方法
第一步:前端控制器接收請求
.action型別的URL通過過濾器進入DispatcherServlet類,呼叫其doDiapatch()方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); ...... }
第二步:前端控制器呼叫處理器對映器查詢 Handler
在doDiapatch()方法中呼叫了DispatcherServlet類的getHandler方法
HandlerExecutionChain mappedHandler = null;
......
mappedHandler = getHandler(processedRequest, false);
其中getHandler方法:@Deprecated
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
return getHandler(request);
}
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
說明對映器根據request當中的URL,找到了Handler,最終返回一個執行器的鏈(HandlerExecutionChain)。這個鏈裡面有Handler。
第三步:呼叫處理器介面卡執行Handler,得到執行結果ModelAndView
ModelAndView mv = null;
......
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
第四步:檢視渲染,將model資料填充到request域。
檢視解析,得到view:
在doDiapatch()方法中有這一句
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
其中processDispatchResult方法
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
其中render(mv, request, response);方法中有
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
渲染方法:
view.render(mv.getModelInternal(), request, response);
呼叫view的渲染方法,將model資料填充到request域
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
//遍歷model裡面的資料,填充到request域
for (Map.Entry<String, Object> entry : model.entrySet()) {
String modelName = entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
if (logger.isDebugEnabled()) {
logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
"] to request in view with name '" + getBeanName() + "'");
}
}
else {
request.removeAttribute(modelName);
if (logger.isDebugEnabled()) {
logger.debug("Removed model object '" + modelName +
"' from request in view with name '" + getBeanName() + "'");
}
}
}
}
大致瞭解了原始碼,便於更好的理解框架。