【Spring源碼這樣讀】-再次走近容器Spring IOC 三

上兩篇,我們已經基本的分析完了XmlBeanFactory的應用實例,本章我們一起來分析一下ApplicationContext。大佬略過

前兩兩篇已經將基本的XmlBeanFactory操作實例講完了,這裏我們來聊聊ApplicationContext。在之前也提過,這兩個東西基本流程上應該是相差不多的。比如我們之前推測:

  • 讀取配置文件
  • 根據配置文件找到對應的類並通過反射來實例化
  • 然後存入容器,被調用的時候取出

ApplicationContext和XmlBeanFactory的區別

在ApplicationContext的源碼解析中,這些推測還能使用嗎?答案是可以的。之前我們聊過IOC容器的設計與實現,無非就是兩個系列,一個BeanFactory,一個ApplicationContext。那麼我們之前看的XmlBeanFactory屬於哪一種類呢?先來看一下XmlBeanFactory的類圖
在這裏插入圖片描述

XmlBeanFactory繼承體系是
XmlBeanFactory -> DefaultListableBeanFactory -> AbstractAutowireCapableBeanFactory -> AbstractBeanFactory -> FactoryBeanRegistrySupport -> DefaultSingletonBeanRegistry -> SimpleAliasRegistry -> AliasRegistry

但是請注意:XmlBeanFactory是在DefaultListableBeanFactory的基礎上做擴展的。其實最終還是實現了BeanFactory接口。而我們的ApplicationContext,它是做爲一種容器的高級形態存在。應用上下文,在簡單的容器上增加了許多其他的特性。爲什麼這麼說呢?我們來看看ApplicationContext的類圖。

在這裏插入圖片描述

不難發現ApplicationContext其實也是實現了BeanFactory,但是這裏可以明顯的看到,ApplicationContext做了更多的支持。

BeanFactory

BeanFactory是Spring最核心的所在,很多面試題問Spring是什麼?其實就可以直接回答:Spring其實就是一個BeanFactory的巨大封裝,當然好像有點片面,BeanFactory確確實實是Spring最核心的內容。

BeanFactory提供的是最基本的IOC容器的功能,關於這些功能,我們可以來讀一下他的源碼

String FACTORY_BEAN_PREFIX = "&";

/**
 * 根據名稱獲取bean
 */
Object getBean(String name) throws BeansException;

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

<T> T getBean(Class<T> requiredType) throws BeansException;

Object getBean(String name, Object... args) throws BeansException;

/**
 * 是否包含bean
 */
boolean containsBean(String name);

/**
 * 查詢bean是否是Singleton類型
 */
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

/**
 * 查詢bean是否是Prototype類型
 */
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

/**
 * 查詢bean的class類型是否是特定的class類型
 */
boolean isTypeMatch(String name, Class<?> targetType)
        throws NoSuchBeanDefinitionException;

/**
 * 查詢bean的class類型
 */
Class<?> getType(String name) throws NoSuchBeanDefinitionException;

/**
 * 查詢bean的所有別名
 */
String[] getAliases(String name);

其實它的代碼並不多,但是它提供了使用IOC容器的規範。理解這些呢,有助於我們理解ApplicationContext,我們可以直接認爲BeanFactory是一種簡單的容器形式,而ApplicationContext它是一種高級的容器形式。

到這裏,其實我們已經看到XmlBeanFactory和ApplicationContext的一些區別了。但是這裏其實不是那麼的明顯。我們不妨再來看看那ApplicationContext它的實現類ClassPathXmlApplicationContext,也就是我們代碼裏面用的。它的一個源碼和XmlBeanFactory的區別在哪

ClassPathXmlApplicationContext

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    super(parent);
    this.setConfigLocations(configLocations);
    if (refresh) {
        this.refresh();
    }
}

它的初始化在不斷的調用super(parent),但是refresh()方法完成了容器的初始化。

refresh()方法的實現代碼如下

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        this.prepareRefresh(); // 準備工作
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); // 獲取ConfigurableListableBeanFactory最終的目的是DefaultListableBeanFactory
        this.prepareBeanFactory(beanFactory); // 準備bean工廠

        try {
            this.postProcessBeanFactory(beanFactory); // 一個空的實現,注意這裏的spring版本號爲:5.3x
            this.invokeBeanFactoryPostProcessors(beanFactory); // 註冊bean的工廠
            this.registerBeanPostProcessors(beanFactory);
            this.initMessageSource(); // Spring 從所有的 @Bean 定義中抽取出來了 BeanPostProcessor,然後都註冊進 beanPostProcessors,等待後面的的順序調用 註冊BeanPostProcessor
            this.initApplicationEventMulticaster(); // 初始化事件監聽多路廣播器
            this.onRefresh(); // 一個空的實現
            this.registerListeners(); // 註冊監聽器
            this.finishBeanFactoryInitialization(beanFactory); // 到了spring加載流程最複雜的一步,開始實例化所有的bd
            this.finishRefresh();// 刷新完成工作
        } catch (BeansException var9) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }

            this.destroyBeans();
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            this.resetCommonCaches();
        }

    }
}

方法從總體上看 還算比較清晰 用幾個大的方法高度概括了refresh()做了些什麼。後面我們會詳細來解讀這些流程,這裏僅做一個總結。

到這裏位置,我們看到了ClassPathXmlApplicationContext的一個流程,和我們的XmlBeanFactory的區別相當的大,在我們之前的推測中,他應該先去加載xml,其實這裏也做了,只是隱藏的比較深,在這個加載流程的第二部的具體內容裏面,我們這裏貼一下核心代碼

ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

this.refreshBeanFactory();

this.loadBeanDefinitions(beanFactory);

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    this.initBeanDefinitionReader(beanDefinitionReader);
    this.loadBeanDefinitions(beanDefinitionReader);
}

其實bean的初始化和容器的初始化也在這個流程裏面全部提現了,這個我們後面再講,但是從我們總結的內容來看,其實和我們的推測是大差不差的,只是多了很多其他的東西而已。多出來的這些東西也正是我們兩種容器實現的區別所在。

總結:

  • BeanFactory的基本實現提供了基本的IOC容器,ApplicaitonContext以高級形態出現,增加了很多附加功能。
  • XmlBeanFactory是我們BeanFactory一種最基本應用
  • XmlBeanFactory和ClassPathXmlApplicationContext都具備我們推測的流程,但是實現有所不同,而且具體流程上,兩個區別很大
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章