SpringMVC之處理請求
接下來將會分析SpringMVC是怎麼處理請求的,首先會分析HttpServletBean、FrameServlet和DispatcherServlet,然後再看一下核心方法doDispatch
HttpServletBean主要參與建立工作,沒有涉及請求的處理
FrameworkServlet:
servlet的處理流程是:首先從Servlet介面的service方法開始,然後在HttpServlet的service方法中根據請求的型別路由到doGet、doPost等方法上:
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲取請求的方法 String method = request.getMethod(); //根據是否是PATCH型別 if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) { processRequest(request, response); } else { super.service(request, response); } }
我們發現在service方法中對於除了PATCH型別,都交給了父類去處理,processRequest方法是處理請求的核心方法:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; //獲取LocaleContextHolder中原來儲存的LocaleContext LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); //獲取當前請求的LocaleContext LocaleContext localeContext = buildLocaleContext(request); //獲取原來儲存的requestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //獲取當前請求的requestAttributes ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //設定當前請求的requestAttributes和localeContext initContextHolders(request, localeContext, requestAttributes); try { //請求處理的入口 doService(request, response); } ... finally { //恢復原來的設定 resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } ... //釋出一個請求處理的訊息 publishRequestHandledEvent(request, response, startTime, failureCause); } }
在這個方法中最終處理還是交給了doService,這個方法留給了DispatcherServlet來實現,在這之前和之後還做了LocaleContext和RequestAttributes的設定及恢復,處理完後釋出ServletRequestHandledEvent訊息以及使用request獲取到了非同步管理器並且註冊了攔截器。
DispatcherServlet
在處理請求的過程中doService是這個Servlet 的入口方法:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, Object> attributesSnapshot = null; //判斷是否是include請求 if (WebUtils.isIncludeRequest(request)) { //對request裡面的屬性進行快照備份 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)); } } } // 設定一些屬性 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()); //用於redirect是的引數傳遞 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()) { // 如果是儲存的快照屬性不為空,需要個request恢復快照屬性 if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
在這個方法中首先判斷當前request是否是include型別的,如果是需要對他裡面的屬性進行快照備份,然後個request設定上一些必要的屬性,然後呼叫doDispatch處理請求,最後如果是include型別的request,需要給它恢復屬性
doDispatch方法最核心的程式碼只有四句:
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
1、根據request找到對應的handler
2、根據handler找到對應的handlerAdapter
3、用handlerAdapter處理handler
4、呼叫processDispatchResult處理上面處理之後的結果
Handler:處理器,直接對應著MVC中的controller層,標註了RequestMapping的所有方法都可以看成一個Handler。只要可以實際處理請求就可以使Handler
HandlerMapping:用來查詢Handler
HandlerAdapter:讓固定的Servlet處理方法呼叫靈活的Handler來進行處理
View:展示資料
ViewResolver:查詢View
來看一下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);
// 根據request找到對應的Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根據Handler找到對應的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//處理get、head請求的Last-Modified
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;
}
}
//執行攔截器的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// HandlerAdapter使用handler處理請求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//如果需要非同步處理直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果view為空,根據request設定預設的view
applyDefaultViewName(request, mv);
//執行攔截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//處理上面的處理結果。包括異常處理,渲染頁面,發出完成通知觸發攔截器的afterCompletion
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
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 刪除上傳請求的資源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch首先會檢查是不是上傳請求,如果是上傳請求,則將request轉換為MultipartHttpServletRequest,並將multipartRequestParsed標誌設定為true,然後通過getHandler方法獲取Handler處理器鏈:
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;
}
在返回的處理器鏈中,包含著一些攔截器和handler就像Pipline中的處理過程一樣
在請求處理完成之後,就要使用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);
}
}
// 渲染頁面
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()) {
return;
}
//請求處理完成之後觸發攔截器AfterCompletion
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
在處理錯誤的時候會將錯誤資訊設定到view中,然後通過render方法渲染頁面,最後觸發攔截器的攔截的事件。