springMVC容器のソースコードの分析をロード


springmvcはservlet容器に基づく軽量でフレキシブルなmvcフレームであり、その要求全体の過程において、多様なニーズを柔軟にカスタマイズできるため、一連のコンポーネントが要求全体のマッピングを完了し、応答などの処理を提供している。ここではspringMVCのソースコードを分析します。
まず、springはすべての要求を処理するservletを提供し、このservletはservletのインターフェースを実現しました。Displatch Servletです。それをweb.xmlに配置して、mvc全体で処理したい要求を処理します。普通は以下のように構成されています。

<servlet>
    <servlet-name>spring-servlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>spring-servlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
Dispartch ServletもFrame ewark Servletの抽象類を継承しています。この抽象類はservlet全体の処理にspring容器のサポートを提供しています。元々servlet容器のDispartServletはspring容器のコンポーネントを利用できる能力を持っています。上のservletの初期化パラメータcontextConfigLocationはDisplatServletがspring容器を取得する配置環境です。Fraameweork Servletはorg.springframe ebook.web.servlet.HttpServletBenから継承されています。HttpServletBenは、servlet容器初期化で呼び出されるinit関数を実現しました。このinit関数はFraameweorkServletの初期化ローディングspring容器法を呼び出します。方法のソースコード:

@Override
  protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
      this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
     //    spring-servlet  
      this.webApplicationContext = initWebApplicationContext();
      initFrameworkServlet();
    }
    catch (ServletException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
    }
    catch (RuntimeException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
    }

    if (this.logger.isInfoEnabled()) {
      long elapsedTime = System.currentTimeMillis() - startTime;
      this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
          elapsedTime + " ms");
    }
  }

ここでスプリングを触発したwebサポートの容器初期化が見られます。ここで使用している容器はWebApplication Contectです。次に、容器全体の初期化過程を分析します。

protected WebApplicationContext initWebApplicationContext() {
//      servlet              Web  
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    //         
    if (this.webApplicationContext != null) {
      // A context instance was injected at construction time -> use it
      wac = this.webApplicationContext;
      if (wac instanceof ConfigurableWebApplicationContext) {
        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
        if (!cwac.isActive()) {
          // The context has not yet been refreshed -> provide services such as
          // setting the parent context, setting the application context id, etc
          if (cwac.getParent() == null) {
            // The context instance was injected without an explicit parent -> set
            // the root application context (if any; may be null) as the parent
            cwac.setParent(rootContext);
          }
          configureAndRefreshWebApplicationContext(cwac);
        }
      }
    }
    if (wac == null) {
      // No context instance was injected at construction time -> see if one
      // has been registered in the servlet context. If one exists, it is assumed
      // that the parent context (if any) has already been set and that the
      // user has performed any initialization such as setting the context id
      wac = findWebApplicationContext();
    }
    if (wac == null) {
      //     
      wac = createWebApplicationContext(rootContext);
    }

    if (!this.refreshEventReceived) {
      // Either the context is not a ConfigurableApplicationContext with refresh
      // support or the context injected at construction time had already been
      // refreshed -> trigger initial onRefresh manually here.
      onRefresh(wac);
    }

    if (this.publishContext) {
      // Publish the context as a servlet context attribute.
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
      if (this.logger.isDebugEnabled()) {
        this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
            "' as ServletContext attribute with name [" + attrName + "]");
      }
    }

    return wac;
  }

すでにコンテナが作成されている場合は初期化します。容器を作成します。論理を作成します。

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    Class<?> contextClass = getContextClass();
    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Servlet with name '" + getServletName() +
          "' will try to create custom WebApplicationContext context of class '" +
          contextClass.getName() + "'" + ", using parent context [" + parent + "]");
    }
    //            
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
      throw new ApplicationContextException(
          "Fatal initialization error in servlet with name '" + getServletName() +
          "': custom WebApplicationContext class [" + contextClass.getName() +
          "] is not of type ConfigurableWebApplicationContext");
    }
    //         ,     ,                 
    ConfigurableWebApplicationContext wac =
        (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

    wac.setEnvironment(getEnvironment());
    wac.setParent(parent);
    wac.setConfigLocation(getContextConfigLocation());
    //                web     
    configureAndRefreshWebApplicationContext(wac);

    return wac;
  }

ここではwebコンテナの関連準備が完了し、設定ファイルの読み込みと初期化容器の本格的な読み込みが開始されます。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//        id
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
      // The application context id is still set to its original default value
      // -> assign a more useful id based on available information
      if (this.contextId != null) {
        wac.setId(this.contextId);
      }
      else {
        // Generate default id...
        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
            ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
      }
    }

    wac.setServletContext(getServletContext());
    wac.setServletConfig(getServletConfig());
    wac.setNamespace(getNamespace());
    wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
      ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
    }
    /*          ,
                                ,
           ,
             ,                   */
    postProcessWebApplicationContext(wac);
    //                  
    applyInitializers(wac);
    //      web      
    wac.refresh();
  }

容器の初期化はAbstractAplicationContectで、他の容器でも最終的にはrefsh()関数に呼び出されます。この関数は基本的に容器初期化の全体の脈絡を定義しています。ここでは展開しません。このブログは後で詳しくこのロジックを分析するはずです。ここで大体の注釈は各関数が完成した操作を説明してください。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      //                          
      prepareRefresh();

      //       
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      //              
      prepareBeanFactory(beanFactory);

      try {
        //                
        postProcessBeanFactory(beanFactory);

        //         ,    invokeBeanDefinitionRegistryPostProcessors invokeBeanFactoryPostProcessors          
        invokeBeanFactoryPostProcessors(beanFactory);

        //   bean           
        registerBeanPostProcessors(beanFactory);

        //                  
        initMessageSource();

        //           
        initApplicationEventMulticaster();

        // Initialize other special beans in specific context subclasses.
        onRefresh();

        //        
        registerListeners();

        //           beans
        finishBeanFactoryInitialization(beanFactory);

        // Last step:                
        finishRefresh();
      }

      //       
      }
    }
  }

その後、コアコンテナをロードし、Frame ewardServletのinitWebAppliation Contect関数に戻り、createWebApplication Comptextを呼び出して一連の上の操作を完了した後、mvc servletコンポーネントが必要となります。入り口はonRefshです。他の方法でinitStrategiesを呼び出します。この方法は以下の通りである

protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    //      RequestMappings
    initHandlerMappings(context);
    //   handler    
    initHandlerAdapters(context);
    //      
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
  }

ここでは、inithandler Mappingsとinithandler Adapters関数について解説します。この二つはservletの要求を処理する入り口です。
spring mvcではどのクラスもrequest要求を処理できます。Dispaccher ServletもHttpServletのインターフェースを実現しているので、処理要求もdoServiceに含まれています。doServiceは要求をdoDisplatch関数に渡します。そしてdoDisplacherのソースコード:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
        processedRequest = checkMultipart(request);
        multipartRequestParsed = (processedRequest != request);

        //            handler
        mappedHandler = getHandler(processedRequest, false);
        if (mappedHandler == null || mappedHandler.getHandler() == null) {
          noHandlerFound(processedRequest, response);
          return;
        }

        /*   handler      ,
             DispacherServlet           ,      HandlerAdapter,
           HandlerMapping          Handler,
          handler       HandlerAdapter      DispacherServlet   。
              HandlerMapping         HandlerAdatper  ,           */
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // Process last-modified header, if supported by the handler.
        String method = request.getMethod();
        boolean isGet = "GET".equals(method);
        if (isGet || "HEAD".equals(method)) {
          long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
          if (logger.isDebugEnabled()) {
            logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
          }
          if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
            return;
          }
        }

        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          return;
        }

        // Actually invoke the handler.
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        if (asyncManager.isConcurrentHandlingStarted()) {
          return;
        }

        applyDefaultViewName(request, mv);
        mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
        dispatchException = ex;
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Error err) {
      triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
    }
    finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
        // Instead of postHandle and afterCompletion
        if (mappedHandler != null) {
          mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
        }
      }
      else {
        // Clean up any resources used by a multipart request.
        if (multipartRequestParsed) {
          cleanupMultipart(processedRequest);
        }
      }
    }
  }

しかし、私たちは、ハンドルはObjectではなく、HandlerExecution Charinであることを発見しました。この種類の中に入ることができます。これはブロックとハンダーのセットです。主に各前置処理を要求する機会です。ここで言及すべきことは、一般的にはブロックとフィルタの違いは、ブロックが後続の実行プロセスを終了することです。フィルタは一般的に終了しません。フィルタは一般的に容器レベルであり、このハンドルフロントスクリーンは、より細かいレベルの制御を行うことができます。例えばフィルタはinitとdofilterのみを定義していますが、このハンドルスクリーンはpreHandleとpostHandleとafterCompletion関数を定義しています。
handlerを処理するget Handlerコードを取得します。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//           ,            handler   
    for (HandlerMapping hm : this.handlerMappings) {
      if (logger.isTraceEnabled()) {
        logger.trace(
            "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      // HandlerExecutionChain   
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
        return handler;
      }
    }
    return null;
  }

HandlerAdapterは処理後、統一されたModelAndViewオブジェクトにパッケージされます。これは試行とデータを含むオブジェクトです。適切な処理が行われています。

processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
ページと返信したデータをブラウザに返して要求処理全体を完了します。以上がspringMVCの大体のスタートプロセスと要求の処理プロセスであり、このブログでは引き続き核心のspringソースを分析しています。博主はよく精読されている有名なフレームのソースコードは短い時間でコードの能力を高める近道だと思っています。デザインも常にその中を貫いています。
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。