1. 程式人生 > >SPRING 啟動載入BEAN 的程式碼過程

SPRING 啟動載入BEAN 的程式碼過程

當Web應用啟動時,contextInitialized方法會執行載入根上下文(IOC容器):

(1)spring的web專案啟動的時候會,啟動我們常用的監聽類。

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/classes/spring-context.xml</param-value>
</context-param>
<!-- Spring監聽器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>


(2)tomcat在載入web應用時候,會針對每個web.xml的監聽器進行建立,看org.apache.catalina.core.StandardContext.listenerStart(),

public boolean listenerStart() {
    if (log.isDebugEnabled())
        log.debug("Configuring application event listeners");


    String listeners[] = findApplicationListeners();
    Object results[] = new Object[listeners.length];
    boolean ok = true;
    for (int i = 0; i < results.length; i++) {
        if (getLogger().isDebugEnabled())
            getLogger().debug(" Configuring event listener class '" + listeners[i] + "'");
        try {
            String listener = listeners[i];
            results[i] = getInstanceManager().newInstance(listener);
        } catch (Throwable t) {
            t = ExceptionUtils.unwrapInvocationTargetException(t);
            ExceptionUtils.handleThrowable(t);
            getLogger().error(sm.getString("standardContext.applicationListener", listeners[i]), t);
            ok = false;
        }
    }
    if (!ok) {
        getLogger().error(sm.getString("standardContext.applicationSkipped"));
        return (false);
    }


    ArrayList<Object> eventListeners = new ArrayList<>();
    ArrayList<Object> lifecycleListeners = new ArrayList<>();
    for (int i = 0; i < results.length; i++) {
        if ((results[i] instanceof ServletContextAttributeListener)
                || (results[i] instanceof ServletRequestAttributeListener)
                || (results[i] instanceof ServletRequestListener)
                || (results[i] instanceof HttpSessionIdListener)
                || (results[i] instanceof HttpSessionAttributeListener)) {
            eventListeners.add(results[i]);
        }
        if ((results[i] instanceof ServletContextListener)
                || (results[i] instanceof HttpSessionListener)) {
            lifecycleListeners.add(results[i]);
        }
    }


    for (Object eventListener : getApplicationEventListeners()) {
        eventListeners.add(eventListener);
    }
    setApplicationEventListeners(eventListeners.toArray());
    
    for (Object lifecycleListener : getApplicationLifecycleListeners()) {
        lifecycleListeners.add(lifecycleListener);
        if (lifecycleListener instanceof ServletContextListener) {
            noPluggabilityListeners.add(lifecycleListener);
        }
    }
    setApplicationLifecycleListeners(lifecycleListeners.toArray());


    if (getLogger().isDebugEnabled())
        getLogger().debug("Sending application start events");


    getServletContext();
    context.setNewServletContextListenerAllowed(false);


    Object instances[] = getApplicationLifecycleListeners();
    if (instances == null || instances.length == 0) {
        return ok;
    }


   

ServletContextEvent event = new ServletContextEvent(getServletContext());
    ServletContextEvent tldEvent = null;
    if (noPluggabilityListeners.size() > 0) {
        noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
        tldEvent = new ServletContextEvent(noPluggabilityServletContext);
    }
    for (int i = 0; i < instances.length; i++) {
        if (!(instances[i] instanceof ServletContextListener))
            continue;
        ServletContextListener listener = (ServletContextListener) instances[i];
        try {
            fireContainerEvent("beforeContextInitialized", listener);
            if (noPluggabilityListeners.contains(listener)) {
                listener.contextInitialized(tldEvent);
            } else {
                listener.contextInitialized(event); 
            }

  fireContainerEvent("afterContextInitialized", listener);//註冊事件
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            fireContainerEvent("afterContextInitialized", listener);
            getLogger().error(sm.getString("standardContext.listenerStart", instances[i].getClass().getName()), t);
            ok = false;
        }
    }
    return (ok);
}

(3)具體的建立wepapplicationcontext的過程如下:

ContextLoaderListener.contextInitialized(ServletContextEvent event) 

 
->ContextLoader.initWebApplicationContext(ServletContext servletContext) 

 
->ContextLoader.createWebApplicationContext(ServletContext sc) - - - - - 如果web.xml裡沒指定型別,預設是XmlWebAppalication


->ContextLoader.configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)  - - - - - -  配置和初始化 webApplicationContext


->ConfigurableWebApplicationContext.refresh() - - - - -  ConfigurableWebApplicationContext是介面


->AbstractApplicationContext.refresh() - - - - - 啟動 容器初始化


->AbstractApplicationContext.obtainFreshBeanFactory() - - - - -  建立BeanFactory,開始容器的初始化過程,比如BeanDefinition的載入


->AbstractRefreshableApplicationContext.refreshBeanFactory() - - - - - 在這裡建立了BeanFactory(DefaultListableBeanFactory)。然後開始loadBeanDefinitions,這是一個抽象方法,實際的載入是在XmlWebApplicationContext類裡。


->XmlWebApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory beanFactory) - - - -建立一個XmlBeanDefinitionReader,並啟動bean定義資訊的載入。


->XmlWebApplicationContext.loadBeanDefinitions(XmlBeanDefinitionReader reader) - - - 根據配置檔案的locations,因為配置檔案是xml的,所以使用的是XmlBeanDefinitionReader 。具體的載入過程是委託給BeanDefinitionReader完成的
 
->AbstractBeanDefinitionReader.loadBeanDefinitionReader - - - 這是一個抽象類,實際的載入是在XmlBeanDefinitionReader中實現的,抽象類是根據bean定義的xml配置檔案的路徑,生成resource。然後XmlBeanDefinitionReader根據resource開始具體載入BeanDefinition


->XmlBeanDefinitionReader.loadBeanDefinitionReader(Resource resource)
 
->XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource) - - - 在讀取器中,需要得到代表XML檔案的resource,這個resource物件封裝了對xml檔案的IO操作,所以讀取器可以在開啟IO流後得到XML的檔案物件。
 
->XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource) - - - -讀取器在這裡開啟IO流後得到XML的檔案Document物件(Document物件是通過XML解析器出來的,下面會再根據Spring Bean的規則再解析出BeanDefinition)。得到物件後就可以按照spring的Bean定義規則來對這個xml的檔案檔樹進行解析了
 
->XmlBeanDefinitionReader.registerBeanDefinitions(Document doc, Resource resource) - - -這個方法啟動對BeanDefinition的解析的詳細過程,這個解析會使用到Spring的Bean的配置規則。具體的過程是有BeanDefinitionDocumentReader來完成的。
->BeanDefinitionDocumentReader.registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
 
->DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(Document doc, XmlReaderContext readerContext) - - - - -DefaultBeanDefinitionDocumentReader 是BeanDefinitionDocumentReader的實現類,registerBeanDefinitions方法開始按照Spring Bean的規則解析Document物件
 
->DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(Element root) - - -解析過程是由BeanDefinitionParserDelegate物件來完成的。


->DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 
->DefaultBeanDefinitionDocumentReader.parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
->DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
 - - - - 解析的結果是由BeanDefinitionHolder來持有的,這個BeanDefinitionHolder物件封裝了BeanDefinition,beanName,aliases等資訊,用它來完成向IOC容器註冊。


->BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele) 
->BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)
->BeanDefinitionParserDelegate.parseBeanDefinitionElement(
               Element ele, String beanName, BeanDefinition containingBean) - - - 對xml配置檔案中的配置的Bean進行詳細解析,這裡只是讀取定義 的<bean>,並載入到BeanDefinition中去,這裡不涉及物件的例項化過程,物件的例項化實際上是再依賴注入時完成的。