SpringMVC解析1-ContextLoaderListener

10665 ワード

https://www.cnblogs.com/wade-luffy/p/6085014.html
SpringMVC機能実装の分析では、まずweb.xmlから始め、web.xmlファイルでContextLoaderListenerを最初に構成します.では、提供された機能はどのように実装されていますか.プログラミング方式を使用すると、spring構成をSpringコンテナに転送できます.たとえば、次のようにします.
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
しかし、Webでは、ContextLoaderListenerを使用してリスニング読み取りを行うには、パスをcontext-paramで登録し、Web環境と組み合わせる必要があります.
ContextLoaderListenerの役割は、Webコンテナを起動するときにApplicationContextの構成情報を自動的にアセンブリすることです.サーブレットContextListenerインタフェースが実装されているため、Web.xmlでこのListenerを構成し、コンテナを起動するとデフォルトで実装方法が実行され、サーブレットContextListenerインタフェースを使用すると、開発者はクライアントにサービスを要求する前にサーブレットContextに任意のオブジェクトを追加することができる.このオブジェクトは、サーブレットContextの起動時に初期化され、サーブレットContextの実行中に表示されます.各Webアプリケーションには、サーブレットContextが関連付けられています.サーブレットContextオブジェクトは、アプリケーションの起動時に作成され、アプリケーションが閉じたときに破棄されます.サーブレットContextは、アプリケーションのグローバル変数と同様に、グローバル範囲で有効です.
サーブレットContextListenerの核心ロジックは、WebApplicationContextインスタンスを初期化し、サーブレットContextに存在させることです.
サーブレットContextListenerの使用
サーブレットContextListenerの使用について説明します.
1カスタムサーブレットContextListener 2登録Listenerテストの作成3テスト
//      ServletContextListener,                ,
//               。          ServletContextListener    contextInitialized  ,
//                    。
public class MyDataContextListener implements ServletContextListener{  
     private ServletContext context = null;  
     public MyDataContextListener(){  
     }  
     //    ServletContext      ,             
     public void contextInitialized(ServletContextEvent event){  
          this.context = event.getServletContext();   
          //                     
          context = setAttribute("myData","this is myData");  
     }  
     //     ServletContext           
     public void contextDestroyed(ServletContextEvent event){  
          this.context = null;  
     }  
}  
// web.xml            
com.test.MydataContextListener  
//   web       ,        Servlet  JSP                  ,  :
//String myData = (String)getServletContext().getAttribute("myData");

SpringのContextLoaderListener
サーブレットContextが起動すると、サーブレットContextListenerのcontextInitializedメソッドが呼び出されます.この関数から分析します.
public void contextInitialized(ServletContextEvent event) {  
    this.contextLoader = createContextLoader();  
    if (this.contextLoader == null) {  
        this.contextLoader = this;  
    }  
    //   WebApplicationContext  
    this.contextLoader.initWebApplicationContext(event.getServletContext());  
}  

ここでは、一般的なクラスWebApplicationContextについて説明します.Webアプリケーションでは、WebApplicationContextを使用します.WebApplicationContextはApplicationContextから継承されています.ApplicationContextには、Springで使用されるClassPathXmlApplicationContextクラスで提供される機能によく似たWeb固有の操作や属性が追加されています.トレースコードを続行:
//initWebApplicationContext          WebApplicationContext         ,                    
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {  
    //WebApplicationContext      。  
    //           ServletContextListener,       Spring     ,                
    // Spring     WebApplicationContext      ServletContext        ,    key  WebApplicationContext.  
    //ROOT_WEB_APPLICSTION_CONTEXT_ATTRIBUTE,          ServletContext         key   。  
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
        throw new IllegalStateException(  
                "Cannot initialize context because there is already a root application context present - " +  
                "check whether you have multiple ContextLoader* definitions in your web.xml!");  
    }  
  
    Log logger = LogFactory.getLog(ContextLoader.class);  
    servletContext.log("Initializing Spring root WebApplicationContext");  
    if (logger.isInfoEnabled()) {  
        logger.info("Root WebApplicationContext: initialization started");  
    }  
    long startTime = System.currentTimeMillis();  
  
    try {  
        // Determine parent for root web application context, if any.  
        ApplicationContext parent = loadParentContext(servletContext);  
        // Store context in local instance variable, to guarantee that  
        // it is available on ServletContext shutdown.  
        //  WebApplicationContext  。  
        this.context = createWebApplicationContext(servletContext, parent);  
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  
        ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
        if (ccl == ContextLoader.class.getClassLoader()) {  
            currentContext = this.context;  
        }  
        else if (ccl != null) {  
            currentContextPerThread.put(ccl, this.context);  
        }  
  
        if (logger.isDebugEnabled()) {  
            logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +  
                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");  
        }  
        if (logger.isInfoEnabled()) {  
            long elapsedTime = System.currentTimeMillis() - startTime;  
            logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");  
        }  
  
        return this.context;  
    }  
    catch (RuntimeException ex) {  
        logger.error("Context initialization failed", ex);  
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);  
        throw ex;  
    }  
    catch (Error err) {  
        logger.error("Context initialization failed", err);  
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);  
        throw err;  
    }  
}  

上のコードで一番重要なのはcreateWebApplicationContextです
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {  
  //  servlet      contextClass,         XmlWebApplicationContext.class
    Class> contextClass = determineContextClass(sc);  
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +  
                "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");  
    }  
  //         contextClass
    ConfigurableWebApplicationContext wac =  
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
    // Assign the best possible id value.  
    if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {  
        // Servlet <= 2.4: resort to name specified in web.xml, if any.  
        String servletContextName = sc.getServletContextName();  
        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
                ObjectUtils.getDisplayString(servletContextName));  
    }  
    else {  
        // Servlet 2.5's getContextPath available!  
        try {  
            String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);  
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +  
                    ObjectUtils.getDisplayString(contextPath));  
        }  
        catch (Exception ex) {  
            throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);  
        }  
    }  
   //parent  ContextLoaderListener      
  // ContextLoaderListener         WebApplicationContext    
    wac.setParent(parent);  
    wac.setServletContext(sc);  
  //  contextConfigLocation  ,   servlet      
    wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));  
    customizeContext(sc, wac);  
    wac.refresh();  
    return wac;  
}  

//  , ContextLoader           
static {  
    // Load default strategy implementations from properties file.  
    // This is currently strictly internal and not meant to be customized  
    // by application developers.  
    try {  
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);  
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);  
    }  
    catch (IOException ex) {  
        throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());  
    }  
}  

以上の静的コードブロックから,属性ファイルContextLoader.propertiesは現在のContextLoaderクラスと同じディレクトリの下に必然的に存在すると推定されるが,見てみるとやはり存在する.コードは以下の通りである.org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext.
 protected Class> determineContextClass(ServletContext servletContext) {
        String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
        if (contextClassName != null) {
            try {
                return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load custom context class [" + contextClassName + "]", ex);
            }
        }
        else {
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            try {
                return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            }
            catch (ClassNotFoundException ex) {
                throw new ApplicationContextException(
                        "Failed to load default context class [" + contextClassName + "]", ex);
            }
        }
    }

まとめ:
WebApplicationContextの存在性の検証.構成でサーブレットContextListenerの宣言は1回のみ許可され、複数回の宣言はSpringの実行ロジックを乱すので、ここではまずこの検証を行います.SpringでWebApplicationContextインスタンスを作成すると、グローバルコールを容易にするためにサーブレットContextに記録されます.keyはWebApplicationContext.ROOTです.WEB_ APPLICATION_CONTEXT_ATTRIBUTEなので、サーブレットContextインスタンスに対応するkeyのプロパティがあるかどうかを確認します.
WebApplicationContextインスタンスを作成します.検証に合格した場合、SpringはWebApplicationContextインスタンスを作成する作業をcreate WebApplicationContext関数に委任します.
初期化の過程で、プログラムはまずContextLoaderクラスとディレクトリの下の属性ファイルContextLoader.propertiesを読み取る.WebApplicationContextインタフェースを実装する実装クラスを抽出し、この実装クラスに基づいて反射的にインスタンスの作成を行うように構成します.
インスタンスをservletContextに記録するcustomizeContext(sc,wac);
現在のクラス・ローダと作成したインスタンスをグローバル変数currentContextPerhreadにマッピングします.