追跡Springソース(一)


一、コンテキストローダ
プロジェクトでSpringフレームワークを使用するには、web.xmlは次のように構成されています.
<!--contextConfigLocation  ContextLoaderListener        /WEB-INF/applicationContext.xml-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
    <!-- <param-value>classpath:applicationContext*.xml</param-value> -->
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

以下はクラスContextLoaderListenerの宣言です.
public class ContextLoaderListener extends ContextLoader implements ServletContextListener{
    //       
    private ContextLoader contextLoader;
          
    //      
    public ContextLoaderListener() {
    }
      
    //       
    public ContextLoaderListener(WebApplicationContext context){
        super(context);
    }
  
    // ......    
}

サーブレットContextListenerは、サーブレットAPIのインタフェースであり、サーブレットContextオブジェクトのライフサイクル、すなわちWebアプリケーション全体のライフサイクルを監視することができます.サーブレットコンテナが起動すると、サーブレットContextEventイベントがトリガーされ、このイベントはサーブレットContextListenerによって処理されます.サーブレットContextListenerインタフェースでは、Webアプリケーションの初期化時のcontextInitializedメソッドと、Webアプリケーションの破棄時のcontextDestroyedメソッドの2つのサーブレットContextEventイベントを処理する方法が定義されています.
/**
 * Initialize the root web application context.
 */
public void contextInitialized(ServletContextEvent event) {
    this.contextLoader = createContextLoader();
    if (this.contextLoader == null) {
        this.contextLoader = this;
    }
    this.contextLoader.initWebApplicationContext(event.getServletContext());
}

上のコードはSpringのContextLoaderListenerに上書きされた初期化リスニング方法であり、Spring作業をサポートするすべての初期化作業は、この方法で完了する.この方法では、最初の行はcreateContextLoader()によってコンテキストのローダを作成しますが、Springでは、この方法の実装はnullを1つだけ返し、コメントではクラスに上書きできることを示しています.一方、この方法はもう時代遅れだ.したがって、初期化作業全体の鍵は、最後の行のコードです.
this.contextLoader.initWebApplicationContext(event.getServletContext());

createContextLoader()メソッドの不作為のため、実際にはthis.contextLoaderはオブジェクト自体のthisです.つまり、createContextLoaderメソッドを上書きしない場合、デフォルトのコンテキストローダはContextLoaderListener自身になります.
二、Web応用コンテキスト
InitWebApplicationContextメソッドについては、ContextLoaderListener自体は実装されていませんが、ContextLoaderから継承されるメソッドです.ソースコードは次のとおりです.
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        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 {
            // Store context in local instance variable, to guarantee that【 context          ,   】
            // it is available on ServletContext shutdown.                【 ServletContext     。】
            if (this.context == null) {
                this.context = createWebApplicationContext(servletContext);
            }
            if (this.context instanceof ConfigurableWebApplicationContext) {
                configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);
            }
            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;
        }
    }

この方法の役割は,主にWebアプリケーションの環境コンテキストcontextを初期化することであり,ContextLoaderはこのようなメンバー変数を持ち,タイプはWebApplicationContextである.この方法では、まず1つのコンテキストローダが存在するかどうかを判断する、すでに存在してローダを初期化すると、IllegalStateException異常が投げ出され、コンテキストの初期化が完了できないことを提示し、webにあるかどうかを確認することを要求する.xmlには複数のコンテキストローダが構成されています.この異常が投げ出されていない場合は、ログの印刷、タイムスタンプの記録などの準備をしますが、これらはこの方法のポイントではありません.
if (this.context == null) {
    this.context = createWebApplicationContext(servletContext); // ①    
}

上のコードはWebアプリケーションコンテキストを作成するキーです.まずここにマークをつけます.メソッドcreateWebApplicationContextのソースコードは次のとおりです.
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    Class<?> contextClass = determineContextClass(sc);
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    return wac;
}

最初の行のコードは、環境コンテキストcontextを作成する方法を決定するために使用されます.たとえば、注釈作成コンテキストまたはXML構成作成コンテキストなどです.この2つの方法は、C o n t i o n C o n f i g WebApplicationContextクラスとXmlWebApplicationContextクラスに対応しています.これらは、C o n f i g u r a b eWebApplicationContextインタフェースを実現しています.明らかに、determineContextClass(sc)がどのような作成方法で対応するクラスを返したのかに注目する必要があります.
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);
        }
    }
}

ソースコードを見ると、コンテキストクラスの取得方法は2つあります.1つは、サーブレットContextから初期化パラメータCONTEXT_を取得することです.CLASS_PARAM(contextClassとして値をとる)は、Web.xmlでcontextノード注入パラメータを構成することができます.(疑わしい).もう1つはdefaultStrategiesからデフォルトのコンテキストクラスを取得することです.まず、デフォルトのアプリケーションコンテキストとは何かに注目します.defaultStrategiesはPropertiesクラスのインスタンスであり、PropertiesクラスはHashMapを継承します.したがって、Properties.getPropertyはHashMap.getに相当します.同時にdefaultStrategiesもContextLoaderの静的メンバー変数です.defaultStrategiesからいくつかのプロパティをgetする場合は、defaultStrategiesの初期化位置を確認する必要があります.
ContextLoaderがdefaultStrategies変数を宣言すると、その変数の初期化コードが次のようになります.
/**
 * Name of the class path resource (relative to the ContextLoader class)
 * that defines ContextLoader's default strategy names.
 */
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
 
private static final Properties defaultStrategies;
  
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());
    }
}

上から分かるように、defaultStrategiesはデフォルトでプロパティファイルを使用して埋め込まれています.ClassPathResourceクラスとP r e tiesLoaderUtils、P r e r tiesLoaderUtilsのloadPropertiesメソッドについては、ここでは続きません.そうしないと、また果てしないパッケージになります.デフォルトのコンテキストクラスが保存されているプロパティファイルを直接見てみましょう.ここにはDEFAULTがありますSTRATEGIES_PATH変数、取値はContextLoader.propertiesは、コンテキストローダのデフォルトポリシーを定義するclass pathリソースファイルの名前(ContextLoaderクラスに対して)を注釈で示しています.spring-web.jarのorg.springframework.web.contextパッケージの下で、このContextLoader.propertiesファイルを簡単に見つけることができます.開くと、次のように表示されます.
# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

したがって、springのデフォルトは、XML構成を使用してコンテキスト環境を作成して初期化することです.
さらにcreateWebApplicationContextメソッドに戻ると、contextClassのデフォルトはXmlWebApplicationContextクラスであり、このクラスのインスタンスを作成する作業、すなわち真のアプリケーション環境コンテキストを作成する作業は、次の世代コードによって行われます.
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

​BeanUtils.instantiateClassは、入力されたタイプとパラメータに基づいて、タイプのインスタンスを構築する反射ベースのツールクラスメソッドです.
これで環境コンテキストの作成が完了し、前のタグの位置1に戻り、その後のコードを追跡し続けることもできます.
本文はブロガーのオリジナル文章で、ブロガーの許可を得ずに転載してはならない.