Spring技術內幕——深刻解析Spring架構與設計原理(四)Web MVC的實現

之前的欠帳,如今補上,歡迎指正和討論。 

Spring Web MVC的實現 
關於MVC,這是和WEB開發相關的部分,顯然你們都是很熟悉了。從最初的JSP到struts,再到像wicket等等,真是百花齊放,百家爭鳴.在WEB UI上,這部分是作web應用架構選擇不可缺乏的一部分。而做爲MVC框架,也許SPRING MVC不能算得上是表現力最出色的UI框架,但無疑,它的實現也是很是的優秀,同時,咱們能夠從它的實現上,看到一個很是清晰的MVC實現的過程,從這點上看,真是很是的過癮啊! 

在瞭解IOC容器的基本實現的基礎上,下面咱們來看看,在典型的Web環境中,Spring IOC容器是如何在Web環境中被載入並起做用的。咱們能夠看到,對於MVC這部分,主要創建在IOC的基礎上,AOP的特性應用得並很少。Spring並非天生就能在Web容器中起做用的,一樣也須要一個啓動過程,把本身的IOC容器導入,並在Web容器中創建起來。 

與對IoC容器的初始化的分析同樣,咱們一樣看到了loadBeanDefinition對BeanDefinition的載入。在Web環境中,對定位BeanDefinition的Resource有特別的要求,對這個要求的處理體如今getDefaultConfigLocations方法的處理中。能夠看到,在這裏,使用了默認的BeanDefinition的配置路徑,這個路徑在XmlWebApplicationContext中,已經做爲一個常量定義好了,這個常量就是/WEB-INF/applicationContext.xml。這裏的loadBeanDefinition實現以下所示: 

web

Java代碼  收藏代碼架構

  1. public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {  app

  2.   

  3.     /** Default config location for the root context */  框架

  4.     //這裏是設置缺省BeanDefinition的地方,在/WEB-INF/applicationContext.xml文件裏,若是不特殊指定其餘文件,IoC容器會從這裏讀取BeanDefinition來初始化IoC容器  ide

  5.     public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";  post

  6.   

  7.     /** Default prefix for building a config location for a namespace */  ui

  8.     public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";  this

  9.   

  10.     /** Default suffix for building a config location for a namespace */  url

  11.     public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";  spa

  12.     //咱們又看到了熟悉的loadBeanDefinition,就像咱們前面對IOC容器的分析同樣,這個加載過程在容器refresh()時啓動。  

  13.     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {  

  14.         // Create a new XmlBeanDefinitionReader for the given BeanFactory.  

  15.         // 對於XmlWebApplicationContext,固然是使用XmlBeanDefinitionReader來對BeanDefinition信息進行解析  

  16.         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  

  17.   

  18.         // Configure the bean definition reader with this context's  

  19.         // resource loading environment.  

  20.         // 這裏設置ResourceLoader,由於XmlWebApplicationContext是DefaultResource的子類,因此這裏一樣會使用DefaultResourceLoader來定位BeanDefinition       

  21.         beanDefinitionReader.setResourceLoader(this);  

  22.         beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  

  23.   

  24.         // Allow a subclass to provide custom initialization of the reader,  

  25.         // then proceed with actually loading the bean definitions.  

  26.         initBeanDefinitionReader(beanDefinitionReader);  

  27.         //這裏使用定義好的XmlBeanDefinitionReader來載入BeanDefinition  

  28.         loadBeanDefinitions(beanDefinitionReader);  

  29.     }  

  30.   

  31.   

  32.     protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {  

  33.     }  

  34.   

  35.   

  36.     //若是有多個BeanDefinition的文件定義,須要逐個載入,都是經過reader來完成的,這個初始化過程是由refreshBeanFactory方法來完成的,這裏只是負責載入BeanDefinition  

  37.     protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  

  38.         String[] configLocations = getConfigLocations();  

  39.         if (configLocations != null) {  

  40.             for (String configLocation : configLocations) {  

  41.                 reader.loadBeanDefinitions(configLocation);  

  42.             }  



進入DispatcherServlet和MVC實現 
完成了在Web環境中,IoC容器的創建之後,也就是在完成對ContextLoaderListener的初始化之後,Web容器開始初始化DispatcherServlet,接着,會執行DispatcherServlet持有的IoC容器的初始化過程,在這個初始化過程當中,一個新的上下文被創建起來,這個DispatcherServlet持有的上下文,被設置爲根上下文的子上下文。能夠大體認爲,根上下文是和Web應用相對應的一個上下文,而DispatcherServlet持有的上下文是和Servlet對應的一個上下文,在一個Web應用中,每每能夠容納多個Servlet存在;與此相對應,對於應用在Web容器中的上下體系,也是很相似的,一個根上下文能夠做爲許多Servlet上下文的雙親上下文。在DispatcherServlet,咱們能夠看到對MVC的初始化,是在DispatcherServlet的initStrategies完成的。 
在這個初始化完成之後,會在上下文中創建器一個執行器於url的對應關係,這個對應關係可讓在url請求到來的時候,MVC能夠檢索到相應的控制器來進行處理,如如下代碼所示: 

Java代碼  收藏代碼

  1. protected Object getHandlerInternal(HttpServletRequest request) throws Exception {  

  2.     //這裏從request中獲得請求的url路徑  

  3.     String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);  

  4.     //這裏使用獲得的url路徑對Handler進行匹配,獲得對應的Handler,若是沒有對應的Hanlder,返回null,這樣默認的Handler會被使用  

  5.     Object handler = lookupHandler(lookupPath, request);  

  6.     if (handler == null) {  

  7.         // We need to care for the default handler directly, since we need to  

  8.         // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.  

  9.         Object rawHandler = null;  

  10.         if ("/".equals(lookupPath)) {  

  11.             rawHandler = getRootHandler();  

  12.         }  

  13.         if (rawHandler == null) {  

  14.             rawHandler = getDefaultHandler();  

  15.         }  

  16.         if (rawHandler != null) {  

  17.             validateHandler(rawHandler, request);  

  18.             handler = buildPathExposingHandler(rawHandler, lookupPath, null);  

  19.         }  

  20.     }  

  21.     if (handler != null && logger.isDebugEnabled()) {  

  22.         logger.debug("Mapping [" + lookupPath + "] to handler '" + handler + "'");  

  23.     }  

  24.     else if (handler == null && logger.isTraceEnabled()) {  

  25.         logger.trace("No handler mapping found for [" + lookupPath + "]");  

  26.     }  

  27.     return handler;  

  28. }  

  29.   // lookupHandler是根據url路徑,啓動在handlerMap中對handler的檢索,並最終返回handler對象  

  30. protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {  

  31.     // Direct match?  

  32.     Object handler = this.handlerMap.get(urlPath);  

  33.     if (handler != null) {  

  34.         validateHandler(handler, request);  

  35.         return buildPathExposingHandler(handler, urlPath, null);  

  36.     }  

  37.     // Pattern match?  

  38.     String bestPathMatch = null;  

  39.     for (String registeredPath : this.handlerMap.keySet()) {  

  40.         if (getPathMatcher().match(registeredPath, urlPath) &&  

  41.                 (bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {  

  42.             bestPathMatch = registeredPath;  

  43.         }  

  44.     }  

  45.     if (bestPathMatch != null) {  

  46.         handler = this.handlerMap.get(bestPathMatch);  

  47.         validateHandler(handler, request);  

  48.         String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);  

  49.         Map<String, String> uriTemplateVariables =  

  50.                 getPathMatcher().extractUriTemplateVariables(bestPathMatch, urlPath);  

  51.         return buildPathExposingHandler(handler, pathWithinMapping, uriTemplateVariables);  

  52.     }  

  53.     // No handler found...  

  54.     return null;  

  55. }  


最後,咱們能夠結合在DispatcherServlet中,對請求的分發處理來了解一個url請求到來時,MVC的實現和協同處理過程,如如下代碼所示: 

Java代碼  收藏代碼

  1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  

  2.     HttpServletRequest processedRequest = request;  

  3.     HandlerExecutionChain mappedHandler = null;  

  4.     int interceptorIndex = -1;  

  5.     //這裏爲視圖準備好一個ModelAndView,這個ModelAndView持有handler處理請求的結果  

  6.     try {  

  7.         ModelAndView mv = null;  

  8.         boolean errorView = false;  

  9.         try {  

  10.             processedRequest = checkMultipart(request);  

  11.             // Determine handler for the current request.  

  12.             // 根據請求獲得對應的handler,hander的註冊以及getHandler的實如今前面已經分析過  

  13.             mappedHandler = getHandler(processedRequest, false);  

  14.             if (mappedHandler == null || mappedHandler.getHandler() == null) {  

  15.                 noHandlerFound(processedRequest, response);  

  16.                 return;  

  17.             }  

  18.             // Apply preHandle methods of registered interceptors.  

  19.             // 調用hander的攔截器,從HandlerExecutionChain中取出Interceptor進行前處理  

  20.             HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();  

  21.             if (interceptors != null) {  

  22.                 for (int i = 0; i < interceptors.length; i++) {  

  23.                     HandlerInterceptor interceptor = interceptors[i];  

  24.                     if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {  

  25.                         triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);  

  26.                         return;  

  27.                     }  

  28.                     interceptorIndex = i;  

  29.                 }  

  30.             }  

  31.             // Actually invoke the handler.  

  32.             // 這裏是實際調用handler的地方,在執行handler以前,用HandlerAdapter先檢查一下handler的合法性:是否是按Spring的要求編寫的handler  

  33.             // handler處理的結果封裝到ModelAndView對象,爲視圖提供展示數據  

  34.             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  

  35.             //這裏經過調用HandleAdapter的handle方法,實際上觸發對Controller的handleRequest方法的調用  

  36.             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  

  37.             // Do we need view name translation?  

  38.             if (mv != null && !mv.hasView()) {  

  39.                 mv.setViewName(getDefaultViewName(request));  

  40.             }  

  41.             // Apply postHandle methods of registered interceptors.  

  42.             if (interceptors != null) {  

  43.                 for (int i = interceptors.length - 1; i >= 0; i--) {  

  44.                     HandlerInterceptor interceptor = interceptors[i];  

  45.                     interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);  

  46.                 }  

  47.             }  

  48.         }  

  49.         catch (ModelAndViewDefiningException ex) {  

  50.             logger.debug("ModelAndViewDefiningException encountered", ex);  

  51.             mv = ex.getModelAndView();  

  52.         }  

  53.         catch (Exception ex) {  

  54.             Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);  

  55.             mv = processHandlerException(processedRequest, response, handler, ex);  

  56.             errorView = (mv != null);  

  57.         }  

  58.         // Did the handler return a view to render?  

  59.         // 這裏使用視圖對ModelAndView數據的展示  

  60.         if (mv != null && !mv.wasCleared()) {  

  61.             render(mv, processedRequest, response);  

  62.             if (errorView) {  

  63.                 WebUtils.clearErrorRequestAttributes(request);  

  64.             }  

  65.         }  

  66.         else {  

  67.             if (logger.isDebugEnabled()) {  

  68.                 logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +  

  69.                         "': assuming HandlerAdapter completed request handling");  

  70.             }  

  71.         }  

  72.         // Trigger after-completion for successful outcome.  

  73.         triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);  

  74.     }  

  75.     catch (Exception ex) {  

  76.         // Trigger after-completion for thrown exception.  

  77.         triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  

  78.         throw ex;  

  79.     }  

  80.     catch (Error err) {  

  81.         ServletException ex = new NestedServletException("Handler processing failed", err);  

  82.         // Trigger after-completion for thrown exception.  

  83.         triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  

  84.         throw ex;  

  85.     }  

  86.     finally {  

  87.         // Clean up any resources used by a multipart request.  

  88.         if (processedRequest != request) {  

  89.             cleanupMultipart(processedRequest);  

  90.         }  

  91.     }  

  92. }  



經過MVC框架,其實是DispatcherServlet的協調運做,獲得了ModelAndView對象做爲數據處理結果,最後,DispatcherServlet把得到的模型數據交給特定的視圖對象,從而完成這些數據的視圖呈現工做,這個視圖呈現由視圖對象的render方法來完成,毫無疑問,對應於不一樣的視圖對象,render方法會完成不一樣的視圖呈現處理,從而爲用戶提供豐富的Web UI表現。關於這些不一樣的視圖展示,還能夠看到不少頗有參考意義的開源軟件的靈活使用,限於篇幅,這裏就不詳細說了。 

對Spring MVC框架的我的理解 

對Spring做爲應用平臺的Web應用開發而言,Spring爲它們提供了Spring MVC框架,做爲一個像struts這樣的Web框架的替代;固然,做爲應用平臺,Spring並不會強制應用對Web框架的選擇。但對Web應用開發而言,選擇直接使用Spring MVC,能夠給應用開發帶來許多便利。由於Spring MVC, 毫無疑問,很好的提供了與Web環境中的IoC容器的集成。同時,和其餘Web應用同樣,使用Spring MVC, 應用只須要專一於處理邏輯和視圖呈現的開發(固然這些開發須要符合Spring MVC的開發習慣),在視圖呈現部分,Spring MVC同時也集成了許多現有的Web UI實現,好比像Excel, PDF這些文檔視圖的生成,由於,集成第三方解決方案,實在能夠說是Spring的拿手好戲,從這種一致性的開發模式上看,它在很大程度上下降了Web應用開發的門檻。 

相關文章
相關標籤/搜索