javascript
Spring MVC的工作原理,我们来看看其源码实现
前言
開心一刻
晚上陪老丈人吃飯,突然手機響了,我手賤按了免提……哥們:快出來喝酒!哥幾個都在呢!我:今天不行,我現在陪老丈人吃飯呢。哥們:那你抓緊喝,我三杯白酒,把我岳父放倒了才出來的,你也快點。看著我老丈人的臉,我不知道該怎么回了……
豬一樣的隊友
遺留問題
在關于利用maven搭建ssm的博客,我們一起來探討下問的最多的問題中,我遺留了一個問題:Spring mvc是何時、何地、如何將Model中的屬性綁定到哪個作用域,這里的作用域指的是Servlet的四大作用域;不了解問題背景的可以回過頭去看看我的上篇博文。
明確的解答我會放到最后,在解答問題之前,我先和大家一起來捋一捋Spring mvc的工作原理。廢話不多說,開始我們神秘的探險之旅!
應用示例
在講工作原理之前,我們先看一個簡單的spring mvc(ssm)示例,以及實現的效果
工程代碼地址:ssm-web
工程結構與效果如上所示,我們不做過多的探究,我們打起精神往下看本篇的重點
工作原理
準備 - 資源的加載與初始化
1、DispatcherServlet 靜態初始化
DispatcherServlet中有如下靜態塊
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.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMappingorg.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapterorg.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager View Code指定了DispatcherServlet策略接口的默認實現,后續DispatcherServlet初始化策略的時候會用到
2、interceptor定義的加載
spring啟動過程中會調用InterceptorsBeanDefinitionParser的parse方法來解析出我們自定義的interceptor定義,封裝成MappedInterceptor類型的bean定義,并放到spring容器中;我們可以簡單的認為spring容器中已經存在了我們自定義的interceptor的bean定義
3、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各個屬性值中
4、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集合組成的一個執行鏈,有點類似FilterChainmappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request. 決定哪個adapter來處理當前的請求// handlerMapping是找出適配的handler,而真正回調handler的是adapterHandlerAdapter 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. 真正調用handlermv = 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);}} } View CodehandlerMapping具體如何找到匹配當前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這個變量,自此疑問得到解答
總結
1、Spring MVC工作原理圖
圖是用的別人的,具體是誰的我也不記得了(捂臉)
2、DefaultAnnotationHandlerMapping在spring3.2中被廢棄,替換成了RequestMappingHandlerMapping
轉載于:https://www.cnblogs.com/youzhibing/p/10695012.html
總結
以上是生活随笔為你收集整理的Spring MVC的工作原理,我们来看看其源码实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows下python虚拟环境vi
- 下一篇: 淘宝服务端高并发分布式架构的十四次演进之