Springソースコード解析(四):Spring MVC


ここではSpring MVCフレームコードを分析します.webAppliationConteetに関する分析は以前の文書を参照してください.Spring Web MVCフレームの実現を重点的に分析します.Displatch Servletの分析から始めます.
//    DispatcherServlet      ,               Spring MVC           
protected void initFrameworkServlet() throws ServletException, BeansException {   
    initMultipartResolver();   
    initLocaleResolver();   
    initThemeResolver();   
    initHandlerMappings();   
    initHandlerAdapters();   
    initHandlerExceptionResolvers();   
    initRequestToViewNameTranslator();   
    initViewResolvers();   
}  

    //    DispatcherServlet      ,               Spring MVC        
    protected void initFrameworkServlet() throws ServletException, BeansException {
        initMultipartResolver();
        initLocaleResolver();
        initThemeResolver();
        initHandlerMappings();
        initHandlerAdapters();
        initHandlerExceptionResolvers();
        initRequestToViewNameTranslator();
        initViewResolvers();
    }
は注釈を見て分かります.これはDispactch Serlvetの初期化過程です.WebAppliation Contectがすでに存在している場合に行われます.すなわち初期化の時にIOC容器はすでに作動しているはずです.これもweb.xmlにSpringを配置する時です.Displatch Servletのload-on-startupの属性を2に配置する必要がある理由.
具体的な初期化過程については分かりやすいです.inithandler Mappingsを持ってみます.
 
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);   
    ........   
    }   
}  

    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環境にロードされている分析を参照してください.Displatch Servletは定義されたすべてのHandlerMappingを一つのListに置いて後で使用します.このチェーンの各要素は一つのhandle Mappingの構成です.一般的に各handler Mappingは一連のURL要求からSpring Controllerへのマッピングを持っています.
HandlerMaapingでは、一連のマッピング関係を持つmapを定義しています.
Displaptcher Servletは、HandlerMappingを通じてWebアプリケーションに実行経路を決定させ、HanderMappingで見たように、HandlerMappingは単なる言い訳である:
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;   
}  

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;
}
彼の具体的な実現には一つのインターフェース方法だけが必要です.このインターフェース方法はHandlerExectionChinで、実際には実行チェーンです.Commandモードで説明したように、この種類は簡単です.つまりInterceptorチェーンと一つのControllerを持つことです.
public class HandlerExecutionChain {   
  
    private Object handler;   
  
    private HandlerInterceptor[] interceptors;   
      
    ........   
}  

public class HandlerExecutionChain {

    private Object handler;

    private HandlerInterceptor[] interceptors;
   
    ........
}
これらのHandlerとIntercepterは私達がHandlerMappingを定義する必要がある時に配置してください.例えば具体的なSimpleURLHandlerMappingに対して、彼がやるべきことはURLマッピングの方式によってHandlerとInterceptorを登録して、自分でマッピングされたhandleMapを維持します.この登録の過程はIOC容器初期化SimpleUrlhandler Mappingの時に完成されました.これからの解析はmapのマッピング情報を使うことができます.ここの情報とbeanファイルの情報は等価です.以下は具体的な登録プロセスです.
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);   
        }   
    }   
}  

    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に登録するコード:
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);   
        ........   
    }   
}  

    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);
            ........
        }
    }
handleMapはHashMapを持っています.中には具体的なマッピング情報が保存されています.
private final Map handlerMap = new HashMap();  

    private final Map handlerMap = new HashMap();
SimpleUrl HandlerMappingのインターフェースHandlerMappingの実装は、このgetHandlerは初期化時に得られたマッピングテーブルに基づいてDisplatch Servletを生成するために必要な実行チェーンです.
 
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);   
}  

    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);
    }
私たちは具体的なハンドル検索過程を見てみます.
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;   
}  

    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;
    }
私たちは常にハンドルMapというHashMapの中で探していますが、直接見つけられなければ、Match Patternのパターンを通じて探してもいいですか?Hnader Mappingを配置する時にANT文法で配置できると覚えています.その中の処理はここにあります.
このように、HandlerMapping全体の初期化プロセスを明確に見ることができます.また、具体的なhandlerマッピングがどのように記憶され、検索されているかを見ました.ここでExecution Chinを生成して、私たちが見つけたhandlerとbeanを定義するときに定義されたInterceptorsを保存します.
Displatch Servletに戻ります.初期化が完了したら、実際のウェブ要求はdoService()方法で処理されます.Displatch Servletは普通のServletだけだと知っています.
 
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);   
        }   
    }   
}  

    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);
            }
        }
    }
要求に対する処理は実際にdoDisplatch()に完成させるものであることを見ました.この方法は長いですが、プロセスは簡単で分かります.
 
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);   
        }   
        .......   
}  

    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です.以下はget Handlerのコードです.
 
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;   
}  

    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;
    }
Servlet Contectでhandleerを取得すれば、直接に戻ってきます.実は、このhandlerは前回の処理の結果をバッファしています.これは初めてServlet Contextに置くべきです.
もしServletContectの中でhandlerを見つけられないなら、持っているhandler Mappingを通じて一つを生成します.今持っているすべてのhandler Mappingを繰り返します.一つだけではないと定義できますので、彼らは定義する時も順序を指定して、最初を見つけてから戻ります.まずハンドルMappingを見つけて、このハンドルMappingを通じて実行チェーンに戻ります.中には最終的なHandlerと私達が定義した一連のInterceptorが含まれています.具体的には上のSimpleUrlhandlerMappingのコード分析を参考にして、getHandlerがどのようにHandlerExecution Charinを得たかを知ることができます.
HandlerExecution Charinを得てから、私達はHandlerAdapterを通じてこのHandlerの合法性を判断します.
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;   
        }   
    }   
    ........   
}  

    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;
            }
        }
        ........
    }
判断によって、私達はこのhandleControllerがControllerインターフェースの実現であるかどうかを知っています.例えば、具体的なHandlerAdapter-SimpleController Handler Adapter:
 
public class SimpleControllerHandlerAdapter implements HandlerAdapter {   
      
    public boolean supports(Object handler) {   
        return (handler instanceof Controller);   
    }   
    .......   
}  

public class SimpleControllerHandlerAdapter implements HandlerAdapter {
   
    public boolean supports(Object handler) {
        return (handler instanceof Controller);
    }
    .......
}
簡単に判断してください.handlerがControllerインターフェースを実現しましたか?これは構成ファイルを検証するための機構をも具現している.
私達はもう一度Displatch Servletに戻ってコードを見ましょう.
 
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  

                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
これはハンドルの具体的な呼び出しです.CommandモードのCommand.executeに相当します.もちろんModelAndViewに戻ります.次はViewを処理するプロセスです.
 
if (mv != null && !mv.wasCleared()) {   
    render(mv, processedRequest, response);   
}  

            if (mv != null && !mv.wasCleared()) {
                render(mv, processedRequest, response);
            }
呼び出されたのはレンダーの方法です.
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);   
 }  

   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がどのように実現されているかを確認できます.
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);   
}  

    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-mergdModelに統合して、それからレンデMergdOutputModelを呼び出します.このrendeMergdOutputModelはテンプレートの方法であり、彼の実現はInternal Resource Viewで、つまりJstlViewの父のクラスである:
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");   
   }   
   .......   

     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");
        }
        .......
    }
まずモデルデータを処理します.exposemodel Arequest AttributesはAbstractViewで実現しました.この方法はModelAndViewのモデルデータと他のrequestデータをすべてServlet Contextに入れます.このようにモデルデータ全体はServlet Contextを通じて暴露して共有されます.
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);   
              .......   
          }   
      }   
  }  

  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で実装されています.
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);   
    }   
  
}  

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、InternalResource Viewのrender過程について比較的に簡単でただ1つの資源のリダイレクト処理を完成します.必要なのは実際にviewのinternalResourceパスを得て、そのリソースに転送することです.どうやってリソースのパスを取得しますか?呼び出しを通じて:
 
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)   
       throws Exception {   
  
   return getUrl();   

     protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)
            throws Exception {

        return getUrl();
    }
このurlはどこで生成されますか?私達はView関連のコードの中で見つけられませんでした.実際に彼はView Rosoliveの時に生成しました.
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;   
}  

    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に関する属性も配置されています.
このView Resoliveはいつ呼び出されましたか?ハッハッ、私達はこのようにまたDisplatch Servletに戻って一体を見に行きます.Displatch Servletで:
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);   
        ..........   
     }   
    ......   
}  

    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);
           ..........
        }
       ......
   }
以下はビュー名を解析するための具体的なプロセスです.
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;   
    }  

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;
    }
ここでは具体的なView Resoloverを呼び出して、ビューの名前を解析します.単純な解析以外に、私たちの要求に基づいて実際に必要なビューオブジェクトを作成しました.具体的なview Resoloverはbean定義ファイルで定義されています.initView Resolover()メソッドではview Resolover変数に初期化されています.具体的なInternal Resource View Resoloverはどのようにビュー名を処理するかを見てみます.Vビューオブジェクトとして生成されます.
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;   
        }   
    }   
}  

    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 hiearhyを見てみます.
そして、私たちはview.renderに戻り、データの最終的なhttpResonseへの書き込みを完了します.例えば、AbstractExcel Viewでの実現:
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();   
}  

    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();
    }
このように、私たちの前の分析と一致しました.Displatch Servletは、解析ビュー名の時に、要求に応じてビューオブジェクトを生成しました.InternalResource Viewに使用するurlと他の各種およびHTTP reponseに関する属性は、生成したビューオブジェクトにそのまま書き込み、ビューオブジェクトのrenderを直接呼び出してデータの展示を完了します.
これはSpring Web MVCフレーム全体の大まかな流れであり、全体のMVCプロセスはDispactch Servletによって制御される.MVCのキープロセスは以下を含む.
handler Mappingは、handlerMappingがチェーンを実行することによって達成されるが、具体的なマッピング関係は、bean定義ファイルで定義され、HandlerMappingがコンテキストをロードするときに配置される.そして、Displatch Servletは、HandlerMappingを呼び出して対応する実行チェーンを得て、最後に図を通してモデルデータを表示しますが、ビューオブジェクトは、ビュー名を解析する際に配置されたものです.これらはコアクラスのHanderMapping、View Resolover、View、Handlerの緊密な協力としてMVCの機能を実現しました.