Spring IOC中的靈魂伴侶:BeanFactory & ApplicationContext

BeanFactory & FactoryBean

提及Spring中的靈魂伴侶難道不是BeanFactoryFactoryBean嗎?ta們兩個不只長相類似,在面試題目中更是如影隨行,成雙成對。然而事實上兩者的關係就像生命中的過客,只是匆匆一眼,便相忘於江湖。
不過FactoryBean並不孤單,遠處的ObjcetFactory遙遙相望,本文中並不許備詳細解析這兩個接口,不過讀者能夠把他們兩個理解爲指定類型Bean的孵化器,咱們能夠經過這兩個接口改變Bean的初始化行爲。可是兩者仍是有很大區別的。html

BeanFactory & ApplicationContext

平常工做中咱們經常將BeanFactory稱爲容器,而將ApplicationContext稱爲上下文。不知你們究竟有沒有思考過兩者之間的關係。Spring Aware接口家族一文中我曾詳細闡釋過如何獲取當前運行環境中的BeanFactoryApplicationContext,咱們不妨在代碼中找尋答案。java

  • 配置類
package com.spring.container;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.spring.container")
public class ContainerConfig {

}
複製代碼
  • 獲取兩者信息
package com.spring.container;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/** * @Author: Raphael */
@Component
public class SpringIoc implements BeanFactoryAware, ApplicationContextAware {

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.err.println(System.identityHashCode(beanFactory));
        System.out.println(beanFactory.getClass().getName());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.err.println(System.identityHashCode(applicationContext));
        System.out.println(applicationContext.getClass().getName());
    }

}
複製代碼
  • 啓動類
package com.spring.container;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/** * @Author: Raphael */
public class MainContainer {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(ContainerConfig.class);
        context.close();
    }

}
複製代碼
  • 結果

顯而易見兩者不只不是一個對象,類型都不同。看到這裏大多數人的認知已經破碎,由於潛意識中他們認爲兩者實際上是一個事物。事實上人家甚至連繼承關係都沒有,直接打碎了另外一波人的幻想。不信咱們看看兩者的UML類圖。

幾個容器?

敏銳的人其實已經發現了矛盾,認知中Spring的根容器應該有且只有一個才合理。可是這分明就是兩個沒有什麼關聯的對象啊。Spring官方文檔如此論述兩者的關係:面試

簡而言之,BeanFactory提供了配置框架和基本功能,ApplicationContext增長了更多針對企業的功能。ApplicationContext是BeanFactory的一個完整的超集。spring

按照官方的解釋:兩者是一個包含與被包含的關係,那麼在ApplicationContext中咱們能夠得到根容器嗎?
上帝說:要有光,因而getAutowireCapableBeanFactory()就來了。設計模式

package com.spring.container;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/** * @Author: Raphael */
@Component
public class SpringIoc implements BeanFactoryAware, ApplicationContextAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.err.println(System.identityHashCode(beanFactory));
        System.out.println(beanFactory.getClass().getName());
    }

    // 直接打印兩個對象的比對結果
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        AutowireCapableBeanFactory factory =
                applicationContext.getAutowireCapableBeanFactory();
        System.err.println(factory.hashCode());
        System.out.println(factory.getClass().getName());
        System.out.println("兩者相等嗎: " + (factory == beanFactory));
    }

}
複製代碼

驗證結果徹底支持官方說明。app

DefaultListableBeanFactory

官方對他如此定義:框架

Spring的{@link ConfigurableListableBeanFactory},{@link BeanDefinitionRegistry}的默認實現
成熟的bean工廠
基於bean定義元數據,可經過後處理器進行擴展ide

臺前光亮的是BeanFactory,負重前行的倒是DefaultListableBeanFactory,明明是三我的的電影,我卻始終不能有姓名。那麼DefaultListableBeanFactory的對象是什麼時候產生的呢?一個新的問題又縈繞在個人心頭。答案其實與IOC容器的初始化密不可分,我在這裏不詳敘了。咱們只簡單的剖析一下DefaultListableBeanFactory產生對象的心路歷程。
咱們在MainContainer中調用了以下構造方法:post

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    register(annotatedClasses);
    refresh();
}
複製代碼

由於AnnotationConfigApplicationContext繼承了GenericApplicationContext,因此父類的的構造方法也會同時調用,容器對象就在此時誕生。ui

public GenericApplicationContext() {
    this.beanFactory = new DefaultListableBeanFactory();
}
複製代碼

而後refresh()調用obtainFreshBeanFactory()

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}
複製代碼

getBeanFactory()AbstractApplicationContext定義的抽象方法。又由GenericApplicationContext實現。

public final ConfigurableListableBeanFactory getBeanFactory() {
    return this.beanFactory;
}
複製代碼

這裏其實就是將本身構造方法產生的對象返回給AnnotationConfigApplicationContext。追蹤到這一層的時候,容器對象的身世之謎才終於被咱們揭開。其實ApplicationContext之因此有Beanfactory能力就是由於有關容器的操做他都委託給本身內部的容器對象了。舉個例子:

public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name, requiredType);
}
複製代碼

這裏實際上他並無對此方法有詳細的實現,而是經過getBeanFactory()獲取自身內部的容器對象,而後交由ta實現。 如今脈絡應該足夠清晰了。Spring源碼是設計模式的集大成者,這裏其實運用的就是組合模式

兩者差別

其實官方有他們兩者的總結

org.springframework.beans和org.springframework.context包是Spring框架的IoC容器的基礎。BeanFactory 接口提供了一種高級配置機制,可以管理任何類型的對象。 ApplicationContext 是其子接口。

增長了如下特性:

  • 與Spring的AOP功能輕鬆集成
  • 消息資源處理(用於國際化)
  • 活動發佈
  • 應用層特定的上下文

Spring官方文檔,真的不許備去看看嘛?

最後: 花長好,月長圓,人長壽

相關文章
相關標籤/搜索