spring源碼分析,view的處理過程

下面我們對Spring MVC框架代碼進行分析,對於webApplicationContext的相關分析可以參見以前的文檔,我們這裏着重分析Spring Web MVC框架的實現.我們從分析DispatcherServlet入手:
Java代碼 複製代碼 收藏代碼
  1. //這裏是對DispatcherServlet的初始化方法,根據名字我們很方面的看到對各個Spring MVC主要元素的初始化   
  2. protected void initFrameworkServlet() throws ServletException, BeansException {   
  3.     initMultipartResolver();   
  4.     initLocaleResolver();   
  5.     initThemeResolver();   
  6.     initHandlerMappings();   
  7.     initHandlerAdapters();   
  8.     initHandlerExceptionResolvers();   
  9.     initRequestToViewNameTranslator();   
  10.     initViewResolvers();   
  11. }  
    //這裏是對DispatcherServlet的初始化方法,根據名字我們很方面的看到對各個Spring MVC主要元素的初始化
    protected void initFrameworkServlet() throws ServletException, BeansException {
        initMultipartResolver();
        initLocaleResolver();
        initThemeResolver();
        initHandlerMappings();
        initHandlerAdapters();
        initHandlerExceptionResolvers();
        initRequestToViewNameTranslator();
        initViewResolvers();
    }

看到註解我們知道,這是DispatcherSerlvet的初始化過程,它是在WebApplicationContext已經存在的情況下進行的,也就意味着在初始化它的時候,IOC容器應該已經工作了,這也是我們在web.xml中配置Spring的時候,需要把DispatcherServlet的 load-on-startup的屬性配置爲2的原因。
對於具體的初始化過程,很容易理解,我們拿initHandlerMappings()來看看:
Java代碼 複製代碼 收藏代碼
  1. private void initHandlerMappings() throws BeansException {   
  2.     if (this.detectAllHandlerMappings) {   
  3.          // 這裏找到所有在上下文中定義的HandlerMapping,同時把他們排序   
  4.          // 因爲在同一個上下文中可以有不止一個handlerMapping,所以我們把他們都載入到一個鏈裏進行維護和管理   
  5.         Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(   
  6.                 getWebApplicationContext(), HandlerMapping.classtruefalse);   
  7.         if (!matchingBeans.isEmpty()) {   
  8.             this.handlerMappings = new ArrayList(matchingBeans.values());   
  9.             // 這裏通過order屬性來對handlerMapping來在list中排序   
  10.             Collections.sort(this.handlerMappings, new OrderComparator());   
  11.         }   
  12.     }   
  13.     else {   
  14.         try {   
  15.             Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);   
  16.             this.handlerMappings = Collections.singletonList(hm);   
  17.         }   
  18.         catch (NoSuchBeanDefinitionException ex) {   
  19.             // Ignore, we'll add a default HandlerMapping later.   
  20.         }   
  21.     }   
  22.   
  23.     //如果在上下文中沒有定義的話,那麼我們使用默認的BeanNameUrlHandlerMapping   
  24.     if (this.handlerMappings == null) {   
  25.         this.handlerMappings = getDefaultStrategies(HandlerMapping.class);   
  26.     ........   
  27.     }   
  28. }  
    private void initHandlerMappings() throws BeansException {
        if (this.detectAllHandlerMappings) {
             // 這裏找到所有在上下文中定義的HandlerMapping,同時把他們排序
             // 因爲在同一個上下文中可以有不止一個handlerMapping,所以我們把他們都載入到一個鏈裏進行維護和管理
            Map matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    getWebApplicationContext(), HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList(matchingBeans.values());
                // 這裏通過order屬性來對handlerMapping來在list中排序
                Collections.sort(this.handlerMappings, new OrderComparator());
            }
        }
        else {
            try {
                Object hm = getWebApplicationContext().getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }

        //如果在上下文中沒有定義的話,那麼我們使用默認的BeanNameUrlHandlerMapping
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(HandlerMapping.class);
        ........
        }
    }

怎樣獲得上下文環境,可以參見我們前面的對IOC容器在web環境中加載的分析。 DispatcherServlet把定義了的所有HandlerMapping都加載了放在一個List裏待以後進行使用,這個鏈的每一個元素都是一個handlerMapping的配置,而一般每一個handlerMapping可以持有一系列從URL請求到 Spring Controller的映射,比如SimpleUrl
HandlerMaaping中就定義了一個map來持有這一系列的映射關係。
DisptcherServlet通過HandlerMapping使得Web應用程序確定一個執行路徑,就像我們在HanderMapping中看到的那樣,HandlerMapping只是一個藉口:
Java代碼 複製代碼 收藏代碼
  1. public interface HandlerMapping {   
  2.   public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =   
  3.                     Conventions.getQualifiedAttributeName(HandlerMapping.class"pathWithinHandlerMapping");   
  4.       //實際上維護一個HandlerExecutionChain,這是典型的Command的模式的使用,這個執行鏈裏面維護handler和攔截器   
  5.     HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;   
  6. }  
public interface HandlerMapping {
  public static final String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE =
                    Conventions.getQualifiedAttributeName(HandlerMapping.class, "pathWithinHandlerMapping");
      //實際上維護一個HandlerExecutionChain,這是典型的Command的模式的使用,這個執行鏈裏面維護handler和攔截器
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

他的具體實現只需要實現一個接口方法,而這個接口方法返回的是一個HandlerExecutionChain,實際上就是一個執行鏈,就像在Command模式描述的那樣,這個類很簡單,就是一個持有一個Interceptor鏈和一個Controller:
Java代碼 複製代碼 收藏代碼
  1. public class HandlerExecutionChain {   
  2.   
  3.     private Object handler;   
  4.   
  5.     private HandlerInterceptor[] interceptors;   
  6.       
  7.     ........   
  8. }  
public class HandlerExecutionChain {

    private Object handler;

    private HandlerInterceptor[] interceptors;
   
    ........
}

而這些Handler和Interceptor需要我們定義HandlerMapping的時候配置好,比如對具體的 SimpleURLHandlerMapping,他要做的就是根據URL映射的方式註冊Handler和Interceptor,自己維護一個放映映射的handlerMap,當需要匹配Http請求的時候需要使用這個表裏的信息來得到執行鏈。這個註冊的過程在IOC容器初始化 SimpleUrlHandlerMapping的時候就被完成了,這樣以後的解析纔可以用到map裏的映射信息,這裏的信息和bean文件的信息是等價的,下面是具體的註冊過程:
Java代碼 複製代碼 收藏代碼
  1. protected void registerHandlers(Map urlMap) throws BeansException {   
  2.     if (urlMap.isEmpty()) {   
  3.         logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");   
  4.     }   
  5.     else {   
  6.         //這裏迭代在SimpleUrlHandlerMapping中定義的所有映射元素   
  7.         Iterator it = urlMap.keySet().iterator();   
  8.         while (it.hasNext()) {   
  9.             //這裏取得配置的url   
  10.             String url = (String) it.next();   
  11.             //這裏根據url在bean定義中取得對應的handler   
  12.             Object handler = urlMap.get(url);   
  13.             // Prepend with slash if not already present.   
  14.             if (!url.startsWith("/")) {   
  15.                 url = "/" + url;   
  16.             }   
  17.             //這裏調用AbstractHandlerMapping中的註冊過程   
  18.             registerHandler(url, handler);   
  19.         }   
  20.     }   
  21. }  
    protected void registerHandlers(Map urlMap) throws BeansException {
        if (urlMap.isEmpty()) {
            logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
        }
        else {
            //這裏迭代在SimpleUrlHandlerMapping中定義的所有映射元素
            Iterator it = urlMap.keySet().iterator();
            while (it.hasNext()) {
                //這裏取得配置的url
                String url = (String) it.next();
                //這裏根據url在bean定義中取得對應的handler
                Object handler = urlMap.get(url);
                // Prepend with slash if not already present.
                if (!url.startsWith("/")) {
                    url = "/" + url;
                }
                //這裏調用AbstractHandlerMapping中的註冊過程
                registerHandler(url, handler);
            }
        }
    }

在AbstractMappingHandler中的註冊代碼:
Java代碼 複製代碼 收藏代碼
  1. protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {   
  2.     //試圖從handlerMap中取handler,看看是否已經存在同樣的Url映射關係   
  3.     Object mappedHandler = this.handlerMap.get(urlPath);   
  4.     if (mappedHandler != null) {   
  5.     ........   
  6.     }   
  7.   
  8.     //如果是直接用bean名做映射那就直接從容器中取handler   
  9.     if (!this.lazyInitHandlers && handler instanceof String) {   
  10.         String handlerName = (String) handler;   
  11.         if (getApplicationContext().isSingleton(handlerName)) {   
  12.             handler = getApplicationContext().getBean(handlerName);   
  13.         }   
  14.     }   
  15.     //或者使用默認的handler.   
  16.     if (urlPath.equals("/*")) {   
  17.         setDefaultHandler(handler);   
  18.     }   
  19.     else {   
  20.    //把url和handler的對應關係放到handlerMap中去   
  21.         this.handlerMap.put(urlPath, handler);   
  22.         ........   
  23.     }   
  24. }  
    protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
        //試圖從handlerMap中取handler,看看是否已經存在同樣的Url映射關係
        Object mappedHandler = this.handlerMap.get(urlPath);
        if (mappedHandler != null) {
        ........
        }

        //如果是直接用bean名做映射那就直接從容器中取handler
        if (!this.lazyInitHandlers && handler instanceof String) {
            String handlerName = (String) handler;
            if (getApplicationContext().isSingleton(handlerName)) {
                handler = getApplicationContext().getBean(handlerName);
            }
        }
        //或者使用默認的handler.
        if (urlPath.equals("/*")) {
            setDefaultHandler(handler);
        }
        else {
       //把url和handler的對應關係放到handlerMap中去
            this.handlerMap.put(urlPath, handler);
            ........
        }
    }

handlerMap是持有的一個HashMap,裏面就保存了具體的映射信息:
Java代碼 複製代碼 收藏代碼
  1. private final Map handlerMap = new HashMap();  
    private final Map handlerMap = new HashMap();

而SimpleUrlHandlerMapping對接口HandlerMapping的實現是這樣的,這個getHandler根據在初始化的時候就得到的映射表來生成DispatcherServlet需要的執行鏈
Java代碼 複製代碼 收藏代碼
  1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {   
  2.     //這裏根據request中的參數得到其對應的handler,具體處理在AbstractUrlHandlerMapping中   
  3.     Object handler = getHandlerInternal(request);   
  4.     //如果找不到對應的,就使用缺省的handler   
  5.     if (handler == null) {   
  6.         handler = this.defaultHandler;   
  7.     }   
  8.     //如果缺省的也沒有,那就沒辦法了   
  9.     if (handler == null) {   
  10.         return null;   
  11.     }   
  12.     // 如果handler不是一個具體的handler,那我們還要到上下文中取   
  13.     if (handler instanceof String) {   
  14.         String handlerName = (String) handler;   
  15.         handler = getApplicationContext().getBean(handlerName);   
  16.     }   
  17.     //生成一個HandlerExecutionChain,其中放了我們匹配上的handler和定義好的攔截器,就像我們在HandlerExecutionChain中看到的那樣,它持有一個handler和一個攔截器組。   
  18.     return new HandlerExecutionChain(handler, this.adaptedInterceptors);   
  19. }  
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //這裏根據request中的參數得到其對應的handler,具體處理在AbstractUrlHandlerMapping中
        Object handler = getHandlerInternal(request);
        //如果找不到對應的,就使用缺省的handler
        if (handler == null) {
            handler = this.defaultHandler;
        }
        //如果缺省的也沒有,那就沒辦法了
        if (handler == null) {
            return null;
        }
        // 如果handler不是一個具體的handler,那我們還要到上下文中取
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }
        //生成一個HandlerExecutionChain,其中放了我們匹配上的handler和定義好的攔截器,就像我們在HandlerExecutionChain中看到的那樣,它持有一個handler和一個攔截器組。
        return new HandlerExecutionChain(handler, this.adaptedInterceptors);
    }

我們看看具體的handler查找過程:
Java代碼 複製代碼 收藏代碼
  1. protected Object getHandlerInternal(HttpServletRequest request) throws Exception {   
  2.     //這裏的HTTP Request傳進來的參數進行分析,得到具體的路徑信息。   
  3.     String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);   
  4.     .......//下面是根據請求信息的查找   
  5.     return lookupHandler(lookupPath, request);   
  6. }   
  7.   
  8. protected Object lookupHandler(String urlPath, HttpServletRequest request) {   
  9.     // 如果能夠直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。   
  10.     Object handler = this.handlerMap.get(urlPath);   
  11.     if (handler == null) {   
  12.         // 這裏使用模式來對map中的所有handler進行匹配,調用了Jre中的Matcher類來完成匹配處理。   
  13.         String bestPathMatch = null;   
  14.         for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {   
  15.             String registeredPath = (String) it.next();   
  16.             if (this.pathMatcher.match(registeredPath, urlPath) &&   
  17.                             (bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {   
  18.                 //這裏根據匹配路徑找到最象的一個   
  19.                 handler = this.handlerMap.get(registeredPath);   
  20.                 bestPathMatch = registeredPath;   
  21.             }   
  22.         }   
  23.   
  24.         if (handler != null) {   
  25.             exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);   
  26.         }   
  27.     }   
  28.     else {   
  29.         exposePathWithinMapping(urlPath, request);   
  30.     }   
  31.     //   
  32.     return handler;   
  33. }  
    protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
        //這裏的HTTP Request傳進來的參數進行分析,得到具體的路徑信息。
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        .......//下面是根據請求信息的查找
        return lookupHandler(lookupPath, request);
    }

    protected Object lookupHandler(String urlPath, HttpServletRequest request) {
        // 如果能夠直接能在SimpleUrlHandlerMapping的映射表中找到,那最好。
        Object handler = this.handlerMap.get(urlPath);
        if (handler == null) {
            // 這裏使用模式來對map中的所有handler進行匹配,調用了Jre中的Matcher類來完成匹配處理。
            String bestPathMatch = null;
            for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
                String registeredPath = (String) it.next();
                if (this.pathMatcher.match(registeredPath, urlPath) &&
                                (bestPathMatch == null || bestPathMatch.length() <= registeredPath.length())) {
                    //這裏根據匹配路徑找到最象的一個
                    handler = this.handlerMap.get(registeredPath);
                    bestPathMatch = registeredPath;
                }
            }

            if (handler != null) {
                exposePathWithinMapping(this.pathMatcher.extractPathWithinPattern(bestPathMatch, urlPath), request);
            }
        }
        else {
            exposePathWithinMapping(urlPath, request);
        }
        //
        return handler;
    }

我們可以看到,總是在handlerMap這個HashMap中找,當然如果直接找到最好,如果找不到,就看看是不是能通過Match Pattern的模式找,我們一定還記得在配置HnaderMapping的時候是可以通過ANT語法進行配置的,其中的處理就在這裏。
這樣可以清楚地看到整個HandlerMapping的初始化過程 - 同時,我們也看到了一個具體的handler映射是怎樣被存儲和查找的 - 這裏生成一個ExecutionChain來儲存我們找到的handler和在定義bean的時候定義的Interceptors.
讓我們回到DispatcherServlet,初始化完成以後,實際的對web請求是在doService()方法中處理的,我們知道DispatcherServlet只是一個普通的Servlet:
Java代碼 複製代碼 收藏代碼
  1. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {   
  2.     .......   
  3.     //這裏把屬性信息進行保存   
  4.     Map attributesSnapshot = null;   
  5.     if (WebUtils.isIncludeRequest(request)) {   
  6.         logger.debug("Taking snapshot of request attributes before include");   
  7.         attributesSnapshot = new HashMap();   
  8.         Enumeration attrNames = request.getAttributeNames();   
  9.         while (attrNames.hasMoreElements()) {   
  10.             String attrName = (String) attrNames.nextElement();   
  11.             if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {   
  12.                 attributesSnapshot.put(attrName, request.getAttribute(attrName));   
  13.             }   
  14.         }   
  15.     }   
  16.   
  17.     // Make framework objects available to handlers and view objects.   
  18.     request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());   
  19.     request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);   
  20.     request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);   
  21.     request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());   
  22.   
  23.     try {   
  24.          //這裏使實際的處理入口   
  25.         doDispatch(request, response);   
  26.     }   
  27.     finally {   
  28.         // Restore the original attribute snapshot, in case of an include.   
  29.         if (attributesSnapshot != null) {   
  30.             restoreAttributesAfterInclude(request, attributesSnapshot);   
  31.         }   
  32.     }   
  33. }  
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        .......
        //這裏把屬性信息進行保存
        Map attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            logger.debug("Taking snapshot of request attributes before include");
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DispatcherServlet.class.getName())) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        // Make framework objects available to handlers and view objects.
        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());

        try {
             //這裏使實際的處理入口
            doDispatch(request, response);
        }
        finally {
            // Restore the original attribute snapshot, in case of an include.
            if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
            }
        }
    }

我們看到,對於請求的處理實際上是讓doDispatch()來完成的 - 這個方法很長,但是過程很簡單明瞭:
Java代碼 複製代碼 收藏代碼
  1. protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {   
  2.     HttpServletRequest processedRequest = request;   
  3.     //這是從handlerMapping中得到的執行鏈   
  4.     HandlerExecutionChain mappedHandler = null;   
  5.     int interceptorIndex = -1;   
  6.       
  7.     ........   
  8.     try {   
  9.         //我們熟悉的ModelAndView開始出現了。   
  10.         ModelAndView mv = null;   
  11.         try {   
  12.             processedRequest = checkMultipart(request);   
  13.   
  14.             // 這是我們得到handler的過程   
  15.             mappedHandler = getHandler(processedRequest, false);   
  16.             if (mappedHandler == null || mappedHandler.getHandler() == null) {   
  17.                 noHandlerFound(processedRequest, response);   
  18.                 return;   
  19.             }   
  20.   
  21.             // 這裏取出執行鏈中的Interceptor進行前處理   
  22.             if (mappedHandler.getInterceptors() != null) {   
  23.                 for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {   
  24.                     HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];   
  25.                     if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {   
  26.                         triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);   
  27.                         return;   
  28.                     }   
  29.                     interceptorIndex = i;   
  30.                 }   
  31.             }   
  32.   
  33.             //在執行handler之前,用HandlerAdapter先檢查一下handler的合法性:是不是按Spring的要求編寫的。   
  34.             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());   
  35.             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());   
  36.   
  37.             // 這裏取出執行鏈中的Interceptor進行後處理   
  38.             if (mappedHandler.getInterceptors() != null) {   
  39.                 for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {   
  40.                     HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];   
  41.                     interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);   
  42.                 }   
  43.             }   
  44.         }   
  45.           
  46.         ........   
  47.   
  48.         // Did the handler return a view to render?   
  49.         //這裏對視圖生成進行處理   
  50.         if (mv != null && !mv.wasCleared()) {   
  51.             render(mv, processedRequest, response);   
  52.         }   
  53.         .......   
  54. }  
    protected void doDispatch(final HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        //這是從handlerMapping中得到的執行鏈
        HandlerExecutionChain mappedHandler = null;
        int interceptorIndex = -1;
       
        ........
        try {
            //我們熟悉的ModelAndView開始出現了。
            ModelAndView mv = null;
            try {
                processedRequest = checkMultipart(request);

                // 這是我們得到handler的過程
                mappedHandler = getHandler(processedRequest, false);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 這裏取出執行鏈中的Interceptor進行前處理
                if (mappedHandler.getInterceptors() != null) {
                    for (int i = 0; i < mappedHandler.getInterceptors().length; i++) {
                        HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
                        if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                            return;
                        }
                        interceptorIndex = i;
                    }
                }

                //在執行handler之前,用HandlerAdapter先檢查一下handler的合法性:是不是按Spring的要求編寫的。
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // 這裏取出執行鏈中的Interceptor進行後處理
                if (mappedHandler.getInterceptors() != null) {
                    for (int i = mappedHandler.getInterceptors().length - 1; i >= 0; i--) {
                        HandlerInterceptor interceptor = mappedHandler.getInterceptors()[i];
                        interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
                    }
                }
            }
           
            ........

            // Did the handler return a view to render?
            //這裏對視圖生成進行處理
            if (mv != null && !mv.wasCleared()) {
                render(mv, processedRequest, response);
            }
            .......
    }

我們很清楚的看到和MVC框架緊密相關的代碼,比如如何得到和http請求相對應的執行鏈,怎樣執行執行鏈和怎樣把模型數據展現到視圖中去。
先看怎樣取得Command對象,對我們來說就是Handler - 下面是getHandler的代碼:
Java代碼 複製代碼 收藏代碼
  1. protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {   
  2.   //在ServletContext取得執行鏈 - 實際上第一次得到它的時候,我們把它放在ServletContext進行了緩存。   
  3.   HandlerExecutionChain handler =   
  4.             (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);   
  5.     if (handler != null) {   
  6.         if (!cache) {   
  7.             request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);   
  8.         }   
  9.         return handler;   
  10.     }   
  11.     //這裏的迭代器迭代的時在initHandlerMapping中載入的上下文所有的HandlerMapping   
  12.     Iterator it = this.handlerMappings.iterator();   
  13.     while (it.hasNext()) {   
  14.         HandlerMapping hm = (HandlerMapping) it.next();   
  15.         .......   
  16.         //這裏是實際取得handler的過程,在每個HandlerMapping中建立的映射表進行檢索得到請求對應的handler   
  17.         handler = hm.getHandler(request);   
  18.   
  19.         //然後把handler存到ServletContext中去進行緩存   
  20.         if (handler != null) {   
  21.             if (cache) {   
  22.                 request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);   
  23.             }   
  24.             return handler;   
  25.         }   
  26.     }   
  27.     return null;   
  28. }  
    protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
      //在ServletContext取得執行鏈 - 實際上第一次得到它的時候,我們把它放在ServletContext進行了緩存。
      HandlerExecutionChain handler =
                (HandlerExecutionChain) request.getAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
        if (handler != null) {
            if (!cache) {
                request.removeAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE);
            }
            return handler;
        }
        //這裏的迭代器迭代的時在initHandlerMapping中載入的上下文所有的HandlerMapping
        Iterator it = this.handlerMappings.iterator();
        while (it.hasNext()) {
            HandlerMapping hm = (HandlerMapping) it.next();
            .......
            //這裏是實際取得handler的過程,在每個HandlerMapping中建立的映射表進行檢索得到請求對應的handler
            handler = hm.getHandler(request);

            //然後把handler存到ServletContext中去進行緩存
            if (handler != null) {
                if (cache) {
                    request.setAttribute(HANDLER_EXECUTION_CHAIN_ATTRIBUTE, handler);
                }
                return handler;
            }
        }
        return null;
    }

如果在ServletContext中可以取得handler則直接返回,實際上這個handler是緩衝了上次處理的結果 - 總要有第一次把這個handler放到ServletContext中去:
如果在ServletContext中找不到handler,那就通過持有的handlerMapping生成一個,我們看到它會迭代當前持有的所有的 handlerMapping,因爲可以定義不止一個,他們在定義的時候也可以指定順序,直到找到第一個,然後返回。先找到一個 handlerMapping,然後通過這個handlerMapping返回一個執行鏈,裏面包含了最終的Handler和我們定義的一連串的 Interceptor。具體的我們可以參考上面的SimpleUrlHandlerMapping的代碼分析知道getHandler是怎樣得到一個 HandlerExecutionChain的。
得到HandlerExecutionChain以後,我們通過HandlerAdapter對這個Handler的合法性進行判斷:
Java代碼 複製代碼 收藏代碼
  1. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {   
  2.     Iterator it = this.handlerAdapters.iterator();   
  3.     while (it.hasNext()) {   
  4.         //同樣對持有的所有adapter進行匹配   
  5.         HandlerAdapter ha = (HandlerAdapter) it.next();   
  6.         if (ha.supports(handler)) {   
  7.             return ha;   
  8.         }   
  9.     }   
  10.     ........   
  11. }  
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        Iterator it = this.handlerAdapters.iterator();
        while (it.hasNext()) {
            //同樣對持有的所有adapter進行匹配
            HandlerAdapter ha = (HandlerAdapter) it.next();
            if (ha.supports(handler)) {
                return ha;
            }
        }
        ........
    }

通過判斷,我們知道這個handler是不是一個Controller接口的實現,比如對於具體的HandlerAdapter - SimpleControllerHandlerAdapter:
Java代碼 複製代碼 收藏代碼
  1. public class SimpleControllerHandlerAdapter implements HandlerAdapter {   
  2.       
  3.     public boolean supports(Object handler) {   
  4.         return (handler instanceof Controller);   
  5.     }   
  6.     .......   
  7. }  
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
   
    public boolean supports(Object handler) {
        return (handler instanceof Controller);
    }
    .......
}

簡單的判斷一下handler是不是實現了Controller接口。這也體現了一種對配置文件進行驗證的機制。
讓我們再回到DispatcherServlet看到代碼:
Java代碼 複製代碼 收藏代碼
  1. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

這個就是對handle的具體調用!相當於Command模式裏的Command.execute();理所當然的返回一個ModelAndView,下面就是一個對View進行處理的過程:
Java代碼 複製代碼 收藏代碼
  1. if (mv != null && !mv.wasCleared()) {   
  2.     render(mv, processedRequest, response);   
  3. }  
            if (mv != null && !mv.wasCleared()) {
                render(mv, processedRequest, response);
            }

調用的是render方法:
Java代碼 複製代碼 收藏代碼
  1. protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)   
  2.          throws Exception {response.setLocale(locale);   
  3.   
  4.      View view = null;   
  5.      //這裏把默認的視圖放到ModelAndView中去。   
  6.      if (!mv.hasView()) {   
  7.          mv.setViewName(getDefaultViewName(request));   
  8.      }   
  9.   
  10.      if (mv.isReference()) {   
  11.          // 這裏對視圖名字進行解析   
  12.          view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);   
  13.      .......   
  14.      }   
  15.      else {   
  16.          // 有可能在ModelAndView裏已經直接包含了View對象,那我們就直接使用。   
  17.          view = mv.getView();   
  18.      ........   
  19.      }   
  20.   
  21.      //得到具體的View對象以後,我們用它來生成視圖。   
  22.      view.render(mv.getModelInternal(), request, response);   
  23.  }  
   protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
            throws Exception {response.setLocale(locale);

        View view = null;
        //這裏把默認的視圖放到ModelAndView中去。
        if (!mv.hasView()) {
            mv.setViewName(getDefaultViewName(request));
        }

        if (mv.isReference()) {
            // 這裏對視圖名字進行解析
            view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
        .......
        }
        else {
            // 有可能在ModelAndView裏已經直接包含了View對象,那我們就直接使用。
            view = mv.getView();
        ........
        }

        //得到具體的View對象以後,我們用它來生成視圖。
        view.render(mv.getModelInternal(), request, response);
    }

從整個過程我們看到先在ModelAndView中尋找視圖的邏輯名,如果找不到那就使用缺省的視圖,如果能夠找到視圖的名字,那就對他進行解析得到實際的需要使用的視圖對象。還有一種可能就是在ModelAndView中已經包含了實際的視圖對象,這個視圖對象是可以直接使用的。
不管怎樣,得到一個視圖對象以後,通過調用視圖對象的render來完成數據的顯示過程,我們可以看看具體的JstlView是怎樣實現的,我們在JstlView的抽象父類 AbstractView中找到render方法:
Java代碼 複製代碼 收藏代碼
  1. public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {   
  2.     ......   
  3.     // 這裏把所有的相關信息都收集到一個Map裏   
  4.     Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));   
  5.     mergedModel.putAll(this.staticAttributes);   
  6.     if (model != null) {   
  7.         mergedModel.putAll(model);   
  8.     }   
  9.   
  10.     // Expose RequestContext?   
  11.     if (this.requestContextAttribute != null) {   
  12.         mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));   
  13.     }   
  14.     //這是實際的展現模型數據到視圖的調用。   
  15.     renderMergedOutputModel(mergedModel, request, response);   
  16. }  
    public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        ......
        // 這裏把所有的相關信息都收集到一個Map裏
        Map mergedModel = new HashMap(this.staticAttributes.size() + (model != null ? model.size() : 0));
        mergedModel.putAll(this.staticAttributes);
        if (model != null) {
            mergedModel.putAll(model);
        }

        // Expose RequestContext?
        if (this.requestContextAttribute != null) {
            mergedModel.put(this.requestContextAttribute, createRequestContext(request, mergedModel));
        }
        //這是實際的展現模型數據到視圖的調用。
        renderMergedOutputModel(mergedModel, request, response);
    }

註解寫的很清楚了,先把所有的數據模型進行整合放到一個Map - mergedModel裏,然後調用renderMergedOutputModel();這個renderMergedOutputModel是一個模板方法,他的實現在InternalResourceView也就是JstlView的父類:
Java代碼 複製代碼 收藏代碼
  1. protected void renderMergedOutputModel(   
  2.        Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {   
  3.   
  4.    // Expose the model object as request attributes.   
  5.    exposeModelAsRequestAttributes(model, request);   
  6.   
  7.    // Expose helpers as request attributes, if any.   
  8.    exposeHelpers(request);   
  9.   
  10.    // 這裏得到InternalResource定義的內部資源路徑。   
  11.    String dispatcherPath = prepareForRendering(request, response);   
  12.   
  13.    //這裏把請求轉發到前面得到的內部資源路徑中去。   
  14.    RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);   
  15.    if (rd == null) {   
  16.        throw new ServletException(   
  17.                "Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");   
  18.    }   
  19.    .......   
     protected void renderMergedOutputModel(
            Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {

        // Expose the model object as request attributes.
        exposeModelAsRequestAttributes(model, request);

        // Expose helpers as request attributes, if any.
        exposeHelpers(request);

        // 這裏得到InternalResource定義的內部資源路徑。
        String dispatcherPath = prepareForRendering(request, response);

        //這裏把請求轉發到前面得到的內部資源路徑中去。
        RequestDispatcher rd = request.getRequestDispatcher(dispatcherPath);
        if (rd == null) {
            throw new ServletException(
                    "Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR");
        }
        .......
    }

首先對模型數據進行處理,exposeModelAsRequestAttributes是在AbstractView中實現的,這個方法把 ModelAndView中的模型數據和其他request數據統統放到ServletContext當中去,這樣整個模型數據就通過 ServletContext暴露並得到共享使用了:
Java代碼 複製代碼 收藏代碼
  1. protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {   
  2.       Iterator it = model.entrySet().iterator();   
  3.       while (it.hasNext()) {   
  4.           Map.Entry entry = (Map.Entry) it.next();   
  5.           ..........   
  6.           String modelName = (String) entry.getKey();   
  7.           Object modelValue = entry.getValue();   
  8.           if (modelValue != null) {   
  9.               request.setAttribute(modelName, modelValue);   
  10.           ...........   
  11.           }   
  12.           else {   
  13.               request.removeAttribute(modelName);   
  14.               .......   
  15.           }   
  16.       }   
  17.   }  
  protected void exposeModelAsRequestAttributes(Map model, HttpServletRequest request) throws Exception {
        Iterator it = model.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            ..........
            String modelName = (String) entry.getKey();
            Object modelValue = entry.getValue();
            if (modelValue != null) {
                request.setAttribute(modelName, modelValue);
            ...........
            }
            else {
                request.removeAttribute(modelName);
                .......
            }
        }
    }

讓我們回到數據處理部分的exposeHelper();這是一個模板方法,其實現在JstlView中實現:
Java代碼 複製代碼 收藏代碼
  1. public class JstlView extends InternalResourceView {   
  2.   
  3.     private MessageSource jstlAwareMessageSource;   
  4.   
  5.   
  6.     protected void initApplicationContext() {   
  7.         super.initApplicationContext();   
  8.         this.jstlAwareMessageSource =   
  9.                 JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());   
  10.     }   
  11.   
  12.     protected void exposeHelpers(HttpServletRequest request) throws Exception {   
  13.         JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);   
  14.     }   
  15.   
  16. }  
public class JstlView extends InternalResourceView {

    private MessageSource jstlAwareMessageSource;


    protected void initApplicationContext() {
        super.initApplicationContext();
        this.jstlAwareMessageSource =
                JstlUtils.getJstlAwareMessageSource(getServletContext(), getApplicationContext());
    }

    protected void exposeHelpers(HttpServletRequest request) throws Exception {
        JstlUtils.exposeLocalizationContext(request, this.jstlAwareMessageSource);
    }

}

在JstlUtils中包含了對於其他而言jstl特殊的數據處理和設置。
過程是不是很長?我們現在在哪裏了?呵呵,我們剛剛完成的事MVC中View的render,對於InternalResourceView的render 過程比較簡單只是完成一個資源的重定向處理。需要做的就是得到實際view的internalResource路徑,然後轉發到那個資源中去。怎樣得到資源的路徑呢通過調用:
Java代碼 複製代碼 收藏代碼
  1. protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)   
  2.        throws Exception {   
  3.   
  4.    return getUrl();   
     protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        return getUrl();
    }

那這個url在哪裏生成呢?我們在View相關的代碼中沒有找到,實際上,他在ViewRosolve的時候就生成了,在UrlBasedViewResolver中:
Java代碼 複製代碼 收藏代碼
  1. protected AbstractUrlBasedView buildView(String viewName) throws Exception {   
  2.     AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());   
  3.     view.setUrl(getPrefix() + viewName + getSuffix());   
  4.     String contentType = getContentType();   
  5.     if (contentType != null) {   
  6.         view.setContentType(contentType);   
  7.     }   
  8.     view.setRequestContextAttribute(getRequestContextAttribute());   
  9.     view.setAttributesMap(getAttributesMap());   
  10.     return view;   
  11. }  
    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
        view.setUrl(getPrefix() + viewName + getSuffix());
        String contentType = getContentType();
        if (contentType != null) {
            view.setContentType(contentType);
        }
        view.setRequestContextAttribute(getRequestContextAttribute());
        view.setAttributesMap(getAttributesMap());
        return view;
    }

這裏是生成View的地方,自然也把生成的url和其他一些和view相關的屬性也配置好了。
那這個ViewResolve是什麼時候被調用的呢?哈哈,我們這樣又要回到DispatcherServlet中去看看究竟,在DispatcherServlet中:
Java代碼 複製代碼 收藏代碼
  1.  protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)   
  2.          throws Exception {   
  3.   
  4.      ........   
  5.      View view = null;   
  6.   
  7.      // 這裏設置視圖名爲默認的名字   
  8.      if (!mv.hasView()) {   
  9.          mv.setViewName(getDefaultViewName(request));   
  10.      }   
  11.   
  12.      if (mv.isReference()) {   
  13.          //這裏對視圖名進行解析,在解析的過程中根據需要生成實際需要的視圖對象。   
  14.          view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);   
  15.         ..........   
  16.      }   
  17.     ......   
  18. }  
    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        ........
        View view = null;

        // 這裏設置視圖名爲默認的名字
        if (!mv.hasView()) {
            mv.setViewName(getDefaultViewName(request));
        }

        if (mv.isReference()) {
            //這裏對視圖名進行解析,在解析的過程中根據需要生成實際需要的視圖對象。
            view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
           ..........
        }
       ......
   }

下面是對視圖名進行解析的具體過程:
Java代碼 複製代碼 收藏代碼
  1. protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)   
  2.             throws Exception {   
  3.          //我們有可能不止一個視圖解析器   
  4.         for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {   
  5.             ViewResolver viewResolver = (ViewResolver) it.next();   
  6.             //這裏是視圖解析器進行解析並生成視圖的過程。   
  7.             View view = viewResolver.resolveViewName(viewName, locale);   
  8.             if (view != null) {   
  9.                 return view;   
  10.             }   
  11.         }   
  12.         return null;   
  13.     }  
protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
            throws Exception {
         //我們有可能不止一個視圖解析器
        for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
            ViewResolver viewResolver = (ViewResolver) it.next();
            //這裏是視圖解析器進行解析並生成視圖的過程。
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
        return null;
    }

這裏調用具體的ViewResolver對視圖的名字進行解析 - 除了單純的解析之外,它還根據我們的要求生成了我們實際需要的視圖對象。具體的viewResolver在bean定義文件中進行定義同時在 initViewResolver()方法中被初始化到viewResolver變量中,我們看看具體的 InternalResourceViewResolver是怎樣對視圖名進行處理的並生成V視圖對象的:對resolveViewName的調用模板在 AbstractCachingViewResolver中,
Java代碼 複製代碼 收藏代碼
  1. public View resolveViewName(String viewName, Locale locale) throws Exception {   
  2.     //如果沒有打開緩存設置,那創建需要的視圖   
  3.     if (!isCache()) {   
  4.         logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");   
  5.         return createView(viewName, locale);   
  6.     }   
  7.     else {   
  8.         Object cacheKey = getCacheKey(viewName, locale);   
  9.         // No synchronization, as we can live with occasional double caching.   
  10.         synchronized (this.viewCache) {   
  11.             //這裏查找緩存裏的視圖對象   
  12.             View view = (View) this.viewCache.get(cacheKey);   
  13.             if (view == null) {   
  14.                 //如果在緩存中沒有找到,創建一個並把創建的放到緩存中去   
  15.                 view = createView(viewName, locale);   
  16.                 this.viewCache.put(cacheKey, view);   
  17.             ........   
  18.             }   
  19.             return view;   
  20.         }   
  21.     }   
  22. }  
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        //如果沒有打開緩存設置,那創建需要的視圖
        if (!isCache()) {
            logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
            return createView(viewName, locale);
        }
        else {
            Object cacheKey = getCacheKey(viewName, locale);
            // No synchronization, as we can live with occasional double caching.
            synchronized (this.viewCache) {
                //這裏查找緩存裏的視圖對象
                View view = (View) this.viewCache.get(cacheKey);
                if (view == null) {
                    //如果在緩存中沒有找到,創建一個並把創建的放到緩存中去
                    view = createView(viewName, locale);
                    this.viewCache.put(cacheKey, view);
                ........
                }
                return view;
            }
        }
    }

關於這些createView(),loadView(),buildView()的關係,我們看看Eclipse裏的call hiearchy
然後我們回到view.render中完成數據的最終對httpResponse的寫入,比如在AbstractExcelView中的實現:
Java代碼 複製代碼 收藏代碼
  1. protected final void renderMergedOutputModel(   
  2.         Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {   
  3.     .........   
  4.     // response.setContentLength(workbook.getBytes().length);   
  5.     response.setContentType(getContentType());   
  6.     ServletOutputStream out = response.getOutputStream();   
  7.     workbook.write(out);   
  8.     out.flush();   
  9. }  
    protected final void renderMergedOutputModel(
            Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        .........
        // response.setContentLength(workbook.getBytes().length);
        response.setContentType(getContentType());
        ServletOutputStream out = response.getOutputStream();
        workbook.write(out);
        out.flush();
    }


這樣就和我們前面的分析一致起來了:DispatcherServlet在解析視圖名的時候就根據要求生成了視圖對象,包括在InternalResourceView中需要使用的url和其他各種和HTTP response相關的屬性都會寫保持在生成的視圖對象中,然後就直接調用視圖對象的render來完成數據的展示。
這就是整個Spring Web MVC框架的大致流程,整個MVC流程由DispatcherServlet來控制。MVC的關鍵過程包括:
配置到handler的映射關係和怎樣根據請求參數得到對應的handler,在Spring中,這是由handlerMapping通過執行鏈來完成的,而具體的映射關係我們在bean定義文件中定義並在HandlerMapping載入上下文的時候就被配置好了。然後 DispatcherServlet調用HandlerMapping來得到對應的執行鏈,最後通過視圖來展現模型數據,但我們要注意的是視圖對象是在解析視圖名的時候生成配置好的。這些作爲核心類的HanderMapping,ViewResolver,View,Handler的緊密協作實現了MVC的功能。

 

轉載地址:http://spring.group.iteye.com/group/wiki/1222

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章