開心一刻html
晚上陪老丈人吃飯,忽然手機響了,我手賤按了免提……哥們:快出來喝酒!哥幾個都在呢!我:今天不行,我如今陪老丈人吃飯呢。哥們:那你抓緊喝,我三杯白酒,把我岳父放倒了纔出來的,你也快點。看着我老丈人的臉,我不知道該怎麼回了……git
豬同樣的隊友web
在關於利用maven搭建ssm的博客,咱們一塊兒來探討下問的最多的問題中,我遺留了一個問題:Spring mvc是什麼時候、何地、如何將Model中的屬性綁定到哪一個做用域,這裏的做用域指的是Servlet的四大做用域;不瞭解問題背景的能夠回過頭去看看個人上篇博文。spring
明確的解答我會放到最後,在解答問題以前,我先和你們一塊兒來捋一捋Spring mvc的工做原理。廢話很少說,開始咱們神祕的探險之旅!tomcat
在講工做原理以前,咱們先看一個簡單的spring mvc(ssm)示例,以及實現的效果mvc
工程代碼地址:ssm-web app
工程結構與效果如上所示,咱們不作過多的探究,咱們打起精神往下看本篇的重點jsp
一、DispatcherServlet 靜態初始化async
DispatcherServlet中有以下靜態塊maven
static { // Load default strategy implementations from properties file. // This is currently strictly internal and not meant to be customized // by application developers. try { ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException ex) { throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage()); } }
這裏會將DispatcherServlet.properties中的內容讀取到DispatcherServlet的屬性:private static final Properties defaultStrategies中,DispatcherServlet.properties內容以下
# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
指定了DispatcherServlet策略接口的默認實現,後續DispatcherServlet初始化策略的時候會用到
二、interceptor定義的加載
spring啓動過程當中會調用InterceptorsBeanDefinitionParser的parse方法來解析出咱們自定義的interceptor定義,封裝成MappedInterceptor類型的bean定義,並放到spring容器中;咱們能夠簡單的認爲spring容器中已經存在了咱們自定義的interceptor的bean定義
三、DispatcherServlet初始化策略:initStrategies
DispatcherServlet的繼承圖以下
DispatcherServlet是一個Servlet,tomcat啓動過程當中會調用其init方法,一串的調用後,會調用DispatcherServlet的initStrategies方法
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
實例化步驟1中的默認實現,並填充到DispatcherServlet各個屬性值中
四、DefaultAnnotationHandlerMapping的攔截器初始化
DispatcherServlet.properties種指定了兩個默認的HandlerMapping:BeanNameUrlHandlerMapping、DefaultAnnotationHandlerMapping,這二者的類繼承圖以下(咱們暫時只關注DefaultAnnotationHandlerMapping)
DefaultAnnotationHandlerMapping間接實現了ApplicationContextAware,那麼在DefaultAnnotationHandlerMapping實例初始化過程當中,會調用setApplicationContext(ApplicationContext applicationContext)方法,一串調用後,會來到AbstractUrlHandlerMapping的initApplicationContext()
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); }
初始化了DefaultAnnotationHandlerMapping的攔截器:interceptor
咱們來看下具體的初始化過程,看看上面的順序是否只是我我的的臆想?
能夠看到,初始化順序就是咱們上面說的,不是我我的的意淫;此時的DefaultAnnotationHandlerMapping中有咱們自定義的MyInterceptor。初始化過程咱們須要關注的就是上述這些,下面咱們一塊兒看看具體請求的過程
請求從servlet的service開始,一路到DispatcherServlet的doDispatch,以下圖
doDispatch
/** * Process the actual dispatching to the handler. 將請求分發到具體的handler,也就是咱們的controller * <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 { processedRequest = checkMultipart(request); multipartRequestParsed = processedRequest != request; // Determine handler for the current request. 決定哪一個handler來處理當前的請求 // mappedHandler是由handler和interceptor集合組成的一個執行鏈,有點相似FilterChain mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. 決定哪一個adapter來處理當前的請求 // handlerMapping是找出適配的handler,而真正回調handler的是adapter 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; } } // handler的前置處理,也就是調用適配當前url的interceptor的preHandler方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } try { // Actually invoke the handler. 真正調用handler mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); } finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } } applyDefaultViewName(request, mv); // handler的後置處理,也就是調用適配當前url的interceptor的postHandler方法 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } // 處理handler返回的結果,會調用適配當前url的interceptor的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 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); return; } // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } }
handlerMapping具體如何找到匹配當前url的handler(通常而言就是咱們的controller)、handlerAdapter具體如何回調真正的handler,有興趣的能夠自行去跟下,我就不跟了。咱們具體看下processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 這個與咱們最初的疑問有關
processDispatchResult
能夠看到model中的persons會被設置到request的attributes中,而後轉發請求到show_person.jsp,轉發過程當中request做用域的變量仍然有效,因此show_person.jsp中的jstl標籤和el表達式可以取到persons變量,最後將show_person.jsp中的內容填充好以後的靜態內容返回給請求者;至此就完成了一次請求的響應
回到咱們開篇的疑問:Spring mvc是什麼時候、何地、如何將Model中的屬性綁定到哪一個做用域?想必你們已經知道答案了
Controller中的model、ModelMap的注入由spring mvc完成,這個不是請求傳入的參數,用於綁定變量到Servlet做用域;默認狀況下,在DispatcherServlet調用了真正的handler以後,將結果返回給請求者的過程當中,將model、modelMap中的變量設置到了request的attributes中,轉發的過程當中,request中的變量仍然有效,因此show_person.jsp中能取到persons這個變量,自此疑問獲得解答
一、Spring MVC工做原理圖
圖是用的別人的,具體是誰的我也不記得了(捂臉)
二、DefaultAnnotationHandlerMapping在spring3.2中被廢棄,替換成了RequestMappingHandlerMapping