Spring源碼學習(1)Xml配置化容器實現

前言
  • 在沒有使用Spring前業務處理過程中業務對象都是通過關鍵字new來創建,雖然可用 static 方法、工具包等形式做一些優化但是還是有局限性。如使用new創建的對象在編譯期定死了使用那個對象不能像Spring那樣可用在運行期決定使用那個對象。再比如在業務處理類中我們可以定義一個接口,在不同的邏輯情況下從Spring容器中獲得不同的實現類來處理邏輯,普通情況下我們可能要new 一堆的對象等待使用(看著實在頭大)。這就是“針對接口編程,而不是針對實現編程”帶來的好處。

Spring通過配置把業務對象托管給Spring容器,這樣我們就不用在手動創建對象了需要用那個對象(默認為單例singleton)直接從Spring容器中取即可。元數據配置如:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>

    <bean id="pxx_test_bean" name="yourMessageSource"
            class="org.springframework.context.support.StaticMessageSource"/>
    <bean  id="pxx_test_user" class="org.springframework.context.support.User">
        <property name="name" value="平行線"></property>
    </bean>
</beans>
  • Spring容器簡單介紹圖如下:
Spring 容器簡介

通過ClassPathXmlApplicationContext源碼查看容器如何實現

如果還不清楚如何導入源碼可以參考我之前分享的GIT + GRADLE+Eclipse 導入Spring 源碼文章。

  • 首先看一下ClassPathXmlApplicationContext結構,如下圖所示:
ClassPathXmlApplicationContext 結構圖
  • ClassPathXmlApplicationContext 的構造函數如下
ClassPathXmlApplicationContext 構造函數截圖
  • 使用關鍵字new創建一個容器代碼如:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
  • ClassPathXmlApplicationContext構造函數 如下:
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
//調用ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)構造函數
        this(configLocations, true, null);
    }
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {
        /**該構造函數主要是設置容器中用戶業務對象的xml配置、
        *Spring容器中ClassLoader、刷新容器獲得
        *內置的工廠類DefaultListableBeanFactory等操作
        */
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

refresh(); 方法詳解

  • refresh()方法代碼如下:
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            
            prepareRefresh();

            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            prepareBeanFactory(beanFactory);

            try {
                postProcessBeanFactory(beanFactory);

                invokeBeanFactoryPostProcessors(beanFactory);

                registerBeanPostProcessors(beanFactory);

                initMessageSource();

                initApplicationEventMulticaster();

                onRefresh();

                registerListeners();

                finishBeanFactoryInitialization(beanFactory);

                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                destroyBeans();

                cancelRefresh(ex);

                throw ex;
            }
        }
    }
  • prepareRefresh();
    主要是上下文的初始值刷新。如啟動時間(startupDate)、當前容器是否已經關閉的標志位(closed初始值為:false),上下文是活躍狀態(active初始值為:true)等。

  •        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    

為容器創建內部的bean工廠。容器中的業務對象由bean工廠管理。如從bean工廠中獲得一個對象代碼片段如下:

public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
        return doGetBean(name, requiredType, args, false);
    }
  • prepareBeanFactory(beanFactory);
    設置bean工廠的類加載器(ClassLoader)、設置bean工廠的bean表達式解析器(StandardBeanExpressionResolver)、環境變量如jdk版本等信息等操作

  • postProcessBeanFactory(beanFactory);
    在容器標準化初始化后修改內部的bean工廠。在所有的業務對象配置都加載,但沒有實例化做特殊處理ClassPathXmlApplicationContext中為空實現。

  • invokeBeanFactoryPostProcessors(beanFactory);
    在容器完成標準初始化后,在所有bean定義都加載了,但是這些bean還沒有被初始化的情況下如果容器中有添加了接口BeanFactoryPostProcessor的實現類那么他們都會在單例實例化前被調用。

invokeBeanFactoryPostProcessors 方法截圖
  • initMessageSource();
    初始化用于保存容器中的格式化好的消息資源。如果容器中存在對應的消息資源這不再重新創建。如容器可以通過先獲得消息實現類如DelegatingMessageSource再通過getMssege()方法獲得最終格式化的數據如"{0}", "{1,date}", "{2,time}等等。

下圖示例DelegatingMessageSource 中的消息最終以key-value 的形式返回調用者。


DelegatingMessageSource 中的消息最終以key-value 的形式保存
  • initApplicationEventMulticaster();
    初始化容器事件管理器,比如容器的事件添加事件移除事件等見如下代碼片段
        //添加事件
        void addApplicationListener(ApplicationListener<?> listener);
        //移除事件
    void removeApplicationListener(ApplicationListener<?> listener);
  • onRefresh();
    ClassPathXmlApplicationContext中為空實現。

  • registerListeners();
    向事件管理器中注冊事件完成特殊邏輯。

  • finishBeanFactoryInitialization(beanFactory);
    示例化剩余的單例對象除了延遲加載(lazy-init="true")的業務對象外 。

  • finishRefresh();
    啟動容器的最后步驟。主要做的4件事情是1:初始化生命周期的處理器。2:刷新生命周期處理器。3:執行事件管理器中注冊的事件。4:如果容器中的環境變量存在“spring.liveBeansView.mbeanDomain” 那么當前應用會保存一份當前容器的一份快照(3.1 或者更高版本)
    代碼片段如下:

protected void finishRefresh() {
        
        initLifecycleProcessor();

        getLifecycleProcessor().onRefresh();

        publishEvent(new ContextRefreshedEvent(this));

        LiveBeansView.registerApplicationContext(this);
    }

總結

通過前面的代碼我們知道了一個以xml配置文件啟動的容器是啟動的全過程,并且可以通過容器獲得不同類型(如全新的,單例的)的業務對象。當然還有很多細節需要詳細探究。比如通過關鍵字 new創建的對象在jvm中是有它的生命周期的,當然Spring容器中的內置bean工廠(接口BeanFactory 的實現類 如 DefaultListableBeanFactory 類)也有一套自己的生命周期(lifecycle)。接下來的文章會一一詳解。

希望對看到這篇文章的小伙伴有所幫助,如果有描述不正確的地方希望不吝賜教。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容