Spring IoC容器與應(yīng)用上下文的設(shè)計與實現(xiàn)

目錄

一、前言
二、Spring IoC容器的設(shè)計
2.1,IoC容器的設(shè)計線路
2.2、應(yīng)用上下文設(shè)計路線
三、IoC容器的具體實現(xiàn)類 DefaultListableBeanFactory(重點)
3.1,作為IoC容器的基礎(chǔ)設(shè)計路線
3.2、作為IoC容器的高級設(shè)計路線
3.3、DefaultListableBeanFactory幾個重要的父類和接口
3.3.1, AbstractBeanFactory 抽象類
3.3.2, AbstractAutowireCapableBeanFactory 抽象類
3.3.3, DefaultSingletonBeanRegistry 讓IoC容器擁有作為“容器”的能力
3.3.4, DefaultListableBeanFactory (重點)
3.3.5, BeanDefinition
四、SpringBoot web工程中的上下文 AnnotationConfigServletWebServerApplicationContext
五、IoC容器的初始化過程
六、IoC容器的依賴注入

一、前言

寫這篇博文的主要目的如下:

  • 通過相關(guān)類和接口分析IoC容器到底長什么樣。
  • 闡述筆者對Spring上下文和容器的理解。
  • 介紹重要的類輔助理解SpringBoot的啟動流程。

二、Spring IoC容器的設(shè)計

看看下面這張圖(摘自《Spring技術(shù)內(nèi)幕》),IoC容器的設(shè)計分為兩條線,

  1. BeanFactory ==> HierarchicalBeanFactory ==>ConfigurableBeanFactory ,這條線可以理解成IoC容器的設(shè)計路線。
  2. BeanFactory ==> ListableBeanFactory ==> ApplicationContext ==> ConfigurableApplicationContext ,這條可以成為Spring應(yīng)用上下文的設(shè)計路線。

為什么這樣要分兩條線呢,主要是將容器和上下文區(qū)分開來。因為在在Spring項目中,上下文對容器不僅是擴(kuò)展的關(guān)系,更重要的是持有的關(guān)系,上下文是以屬性的形式持有了容器,開發(fā)者可以通過上下文對象獲取到容器。筆者十分傾向于將二者分開來理解。當(dāng)然也可以將應(yīng)用上下文理解成容器的高級表現(xiàn)形式。

image

2.1,IoC容器的設(shè)計線路

BeanFactory定義了IoC容器的基本規(guī)范,包括getBean()按類型和按名稱的獲取Bean的方法。

image
image

HierarchicalBeanFactory 在BeanFactory的基礎(chǔ)上增加了getParentBeanFactory()方法,使BeanFactory具備了雙親IoC容器管理的功能。

ConfigurableBeanFactory接口提供了配置BeanFactory的各種方法。比如setParentBeanFactory()方法,配置上面提到的雙親IoC容器,addBeanPostProcessor()方法,配置Bean后置處理器等。

到這里先埋個包袱:到ConfigurableBeanFactory接口為止,IoC容器還沒有具備作為“容器”最基本的功能,那就是能裝東西。

2.2、應(yīng)用上下文設(shè)計路線

上面說了應(yīng)用上下文是IoC容器的高級表現(xiàn)形式,ListableBeanFactory具備了操作BeanDefinition 的能力,比如getBeanDefinitionCount()方法,可以獲取Bean的總數(shù)等。

ApplicationContext 類那就厲害了,如下代碼所示,實現(xiàn)了一大堆接口

public interface ApplicationContext extends 
    EnvironmentCapable, 
    ListableBeanFactory, 
    HierarchicalBeanFactory,  
    MessageSource, 
    ApplicationEventPublisher, 
    ResourcePatternResolver
  • MessageSource,支持不同的信息源。具備支持國際化的實現(xiàn),為開發(fā)多語言版本的應(yīng)用提供服務(wù)。
  • ResourcePatternResolver,訪問數(shù)據(jù)源。具備了從不同地方得到Bean定義資源的能力,比如:xml,java config,注解等等。
  • ApplicationEventPublisher,發(fā)布事件。使應(yīng)用上下文具備了事件機(jī)制。事件機(jī)制為Bean聲明周期的管理提供了便利。

WebApplicationContext擴(kuò)展了對web應(yīng)用的支持。

ConfigurableApplicationContext就更重要了,看過Spring源碼的都知道一個重要的方法叫refresh,沒錯就是在這個接口中定義的。最重要的是擴(kuò)展了配置上下文的功能,和控制上下文生命周期的能力等等。

image

三、IoC容器的具體實現(xiàn)類 DefaultListableBeanFactory(重點)

首先證明一點,為什么說DefaultListableBeanFactory類是具體的實現(xiàn)類呢?

隨便啟動一個SpringBoot項目找到第25行代碼(在SpringBoot的啟動流程系列博文中有介紹)

public ConfigurableApplicationContext run(String... args) {
    //記錄程序運(yùn)行時間
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // ConfigurableApplicationContext Spring 的上下文
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    //從META-INF/spring.factories中獲取監(jiān)聽器
    //1、獲取并啟動監(jiān)聽器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);
        //2、構(gòu)造容器環(huán)境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //處理需要忽略的Bean
        configureIgnoreBeanInfo(environment);
        //打印banner
        Banner printedBanner = printBanner(environment);
        ///3、初始化容器
        context = createApplicationContext();
        //實例化SpringBootExceptionReporter.class,用來支持報告關(guān)于啟動的錯誤
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[]{ConfigurableApplicationContext.class}, context);
        //4、刷新容器前的準(zhǔn)備階段
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //5、刷新容器
        refreshContext(context);
        //刷新容器后的擴(kuò)展接口
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

debug

image

如2標(biāo)注點所示IoC容器的真實面孔就是這個DefaultListableBeanFactory類了。當(dāng)然他還有一個子類XmlBeanFactory,不過都已經(jīng)被標(biāo)注為棄用了(@Deprecated)在《Spring技術(shù)內(nèi)幕》這本書里面也是著重的講的這個類,可能當(dāng)時作者是以SpringMVC項目來講解的吧。XmlBeanFactory顧名思義就是提供了對xml配置方式的支持。呃。。。又勾起了用SpringMVC的痛苦回憶。

image

言歸正傳,

如下圖,看看他的繼承關(guān)系

image

章節(jié)二中提到了很多的IoC容器系列,這樣總結(jié)一下吧,俗話說一流企業(yè)做標(biāo)準(zhǔn),二流企業(yè)做產(chǎn)品,章節(jié)二中的那一坨就是IoC容器的實現(xiàn)標(biāo)準(zhǔn),本章節(jié)我們要總結(jié)的類DefaultListableBeanFactory就是IoC容器的具體產(chǎn)品。

看見上圖中那一堆接口和類是不是有點懵,沒關(guān)系,咱們慢慢梳理一下。

3.1,作為IoC容器的基礎(chǔ)設(shè)計路線

image

這條線路在上一章節(jié)中已經(jīng)梳理過了。只是多出了ConfigurableListableBeanFactory接口,ConfigurableListableBeanFactory接口主要是增加指定忽略類型和接口等

image

3.2、作為IoC容器的高級設(shè)計路線

image

這條設(shè)計路線乍一看還是挺復(fù)雜的,的確是這樣。

1, BeanFactory ==> AutowireCapableBeanFactory ==> AbstractAutowireCapableBeanFactory ==> DefaultListableBeanFactory

在這條線路中,AutowireCapableBeanFactory接口定義了自動注入bean(autowireBean()),創(chuàng)建bean(createBean()),初始化bean(initializeBean())方法等。那么真正實現(xiàn)這些方法的類便是AbstractAutowireCapableBeanFactory。

AbstractAutowireCapableBeanFactory抽象類中實現(xiàn)了AutowireCapableBeanFactory接口定義的方法。在此基礎(chǔ)上通過繼承AbstractBeanFactory具備了操作Bean的能力。

2, SingletonBeanRegistry ==> DefaultSingletonBeanRegistry ==> FactoryBeanRegistrySupport ==> AbstractBeanFactory ==> AutowireCapableBeanFactory ==> DefaultListableBeanFactory

這條關(guān)系鏈有點長,在這條鏈中我們要關(guān)心的是SingletonBeanRegistry接口,顧名思義,這個接口是單例Bean的注冊接口。當(dāng)然也不止注冊這么簡單。如下圖中所示,除了注冊單例之外,還定義獲取單例的方法。

注意:為什么只有singleton的注冊中心,而沒有prototype類型的Bean的注冊中心呢?因為單例Bean(singleton)是Spring幫我們創(chuàng)建的并維護(hù)的,原型Bean(prototype)是每次獲取都會創(chuàng)建出來一個實例。本質(zhì)是不同的。

image

3, AliasRegistry ==> SimpleAliasRegistry ==> DefaultSingletonBeanRegistry ==> FactoryBeanRegistrySupport ==> AbstractBeanFactory ==> AutowireCapableBeanFactory ==> DefaultListableBeanFactory

這條路線呢,主要是提供管理別稱的能力。因為不是重點,在此就不詳細(xì)分析了。

4, AliasRegistry ==> BeanDefinitionRegistry ==> DefaultListableBeanFactory

BeanDefinitionRegistry接口要重點說一下,該接口是BeanDefinition的注冊中心。使DefaultListableBeanFactory具備操作BeanDefinition的能力。看一下它有什么方法。

image

包括了注冊,刪除,獲取BeanDefinition的方法。當(dāng)然這只是個接口,這些方法的具體實現(xiàn)在DefaultListableBeanFactory中。

3.3、DefaultListableBeanFactory幾個重要的父類和接口

3.3.1, AbstractBeanFactory 抽象類

image

如上圖所示,AbstractBeanFactory中實現(xiàn)了BeanFactory中定義的幾個重要的方法。常用的注解 @Autowired @Resource(name = "xxx") 大家都知道一個是按類查找,一個是按名獲取。具體實現(xiàn)這兩個注解的方法就是上圖中圈出來的幾個方法。幾個getBean()方法最終都進(jìn)入了doGetBean()方法。doGetBean()方法是實際獲得Bean的地方,也是觸發(fā)依賴注入發(fā)生的地方。在SpringBoot啟動流程總會對這個方法進(jìn)行詳細(xì)的介紹。

3.3.2, AbstractAutowireCapableBeanFactory 抽象類

AbstractBeanFactory中實現(xiàn)了getBean()方法,AbstractAutowireCapableBeanFactory中實現(xiàn)了Bean的創(chuàng)建方法。

當(dāng)我們需要定義一個Bean通常會有這樣寫 @Bean(name = "test", initMethod = "init", destroyMethod = "destroy") 。AbstractAutowireCapableBeanFactory中完成了一個Bean從 create(createBean()) ==> createInstance(createBeanInstance()) ==> init(invokeInitMethods()) 的所有工作。所以這個抽象類的作用不言而喻。具體的創(chuàng)建過程,會在SpringBoot的啟動流程中詳細(xì)介紹。

3.3.3, DefaultSingletonBeanRegistry 讓IoC容器擁有作為“容器”的能力

其實我們經(jīng)常說的Spring 容器,這個容器其實更多的是BeanFactory所代表的意義:Bean生產(chǎn)工廠。是滴,通常我們理解的容器應(yīng)該是能裝東西的,但是spring 容器不是代表代表水桶一樣的東西,而是像富士康一樣,生產(chǎn)東西的地方。比如我們需要一個Bean,我們只需要告訴spring,spring就會給我們。所以到目前為止我們還沒有看到IoC作為“容器”的能力。以上言論純屬自己理解,不喜勿噴。

DefaultSingletonBeanRegistry屬性先貼出來

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    /**
     * Cache of singleton objects: bean name --> bean instance
     * 緩存 單例對象
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);//好習(xí)慣,創(chuàng)建ConcurrentHashMap,指定初始化因子,縱觀Spring源碼,創(chuàng)建HashMap,都有初始化因子。get

    /**
     * Cache of singleton factories: bean name --> ObjectFactory
     * 緩存 單例工廠
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /**
     * Cache of early singleton objects: bean name --> bean instance
     * 緩存 提前暴露的對象
     */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    /**
     * Set of registered singletons, containing the bean names in registration order
     * 已經(jīng)注冊的單例對象集合,按照注冊順序排序的,并且是不可重復(fù)的。
     */
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

    /**
     * Names of beans that are currently in creation
     *
     */
    private final Set<String> singletonsCurrentlyInCreation =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    /**
     * Names of beans currently excluded from in creation checks
     */
    private final Set<String> inCreationCheckExclusions =
            Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    /**
     * List of suppressed Exceptions, available for associating related causes
     */
    @Nullable
    private Set<Exception> suppressedExceptions;

    /**
     * Flag that indicates whether we're currently within destroySingletons
     */
    private boolean singletonsCurrentlyInDestruction = false;

    /**
     * Disposable bean instances: bean name --> disposable instance
     * spring是作為一個注冊中心的樣子,在容器shutdown的時候,直接從這里面找到需要執(zhí)行destory鉤子的Bean
     */
    private final Map<String, Object> disposableBeans = new LinkedHashMap<>();

    /**
     * Map between containing bean names: bean name --> Set of bean names that the bean contains
     * 名稱為name的bean,所持有的beans 的映射關(guān)系
     */
    private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);

    /**
     * Map between dependent bean names: bean name --> Set of dependent bean names
     * 名稱為name的bean與其所依賴的bean的映射關(guān)系
     */
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

    /**
     * Map between depending bean names: bean name --> Set of bean names for the bean's dependencies
     */
    private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
}

屬性singletonObjects ,沒錯,就是這個東東,最終存儲單例(singleton)Bean的地方,在SpringBoot啟動流程中,會詳細(xì)介紹存取的過程。上面說了原型(prototype)Bean是不需要緩存的,不解釋了。到這里我們初步看到了IoC作為“容器”該有的樣子。

DefaultSingletonBeanRegistry上面提到的SingletonBeanRegistry接口的相關(guān)方法,并且增加了很多對單例的操作的方法。

3.3.4, DefaultListableBeanFactory (重點)

上面我們從IoC容器的宏觀設(shè)計角度闡述了DefaultListableBeanFactory作為IoC容器的具體實現(xiàn)的設(shè)計思想。在這里來分析一下這個類本身的設(shè)計。

首先看看該類的屬性

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    /**
     * Map from serialized id to factory instance
     * 緩存 序列化ID到 DefaultListableBeanFactory 實例的映射
     */
    private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
            new ConcurrentHashMap<>(8);

    /**
     * Optional id for this factory, for serialization purposes
     */
    @Nullable
    private String serializationId;

    /**
     * Whether to allow re-registration of a different definition with the same name
     */
    private boolean allowBeanDefinitionOverriding = true;

    /**
     * Whether to allow eager class loading even for lazy-init beans
     */
    private boolean allowEagerClassLoading = true;

    /**
     * Optional OrderComparator for dependency Lists and arrays
     */
    @Nullable
    private Comparator<Object> dependencyComparator;

    /**
     * Resolver to use for checking if a bean definition is an autowire candidate
     * 被用來解決去校驗一個BeanDefinition是不是自動裝載的候選人
     */
    private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();

    /**
     * Map from dependency type to corresponding autowired value
     * 緩存 類型對應(yīng)的自動裝載的Bean
     */
    private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);

    /**
     * Map of bean definition objects, keyed by bean name
     * 緩存 beanName到BeanDefinition的映射關(guān)系
     */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    /**
     * Map of singleton and non-singleton bean names, keyed by dependency type
     * 緩存 類型 和 beanName的映射關(guān)系
     */
    private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);

    /**
     * Map of singleton-only bean names, keyed by dependency type
     * 緩存 類型 和 單例Bean names的映射
     */
    private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);

    /**
     * List of bean definition names, in registration order
     * 緩存 beanDefinition name的list
     */
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

    /**
     * List of names of manually registered singletons, in registration order
     */
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);

    /**
     * Cached array of bean definition names in case of frozen configuration
     */
    @Nullable
    private volatile String[] frozenBeanDefinitionNames;

    /**
     * Whether bean definition metadata may be cached for all beans
     */
    private volatile boolean configurationFrozen = false;
}

在Spring中,實際上是把DefaultListableBeanFactory作為一個默認(rèn)的功能完整的IoC容器來使用。 DefaultListableBeanFactory作為一個功能完整的容器具備了除以上父類所具有功能外,還加入了對BeanDefinition的管理和維護(hù)。從上面的代碼可以看到一個重要的屬性:beanDefinitionMap。beanDefinitionMap緩存了Bean name到 BeanDefinition的映射。到這里是不是發(fā)現(xiàn)了IoC容器另外一個作為“容器”的能力。

在我的理解范圍內(nèi),IoC容器作為“容器”真正裝的兩個最總要的能力算是總結(jié)完了,一個是裝單例(Singleton)Bean,一個是裝BeanDefinition。

3.3.5, BeanDefinition

Spring通過定義BeanDefinition來管理基于Spring的應(yīng)用中的各種對象以及他們之間的相互依賴關(guān)系。BeanDefinition抽象了我們對Bean的定義,是讓容器起作用的主要數(shù)據(jù)類型。我么都知道在計算機(jī)世界里,所有的功能都是建立在通過數(shù)據(jù)對現(xiàn)實進(jìn)行抽象的基礎(chǔ)上的。IoC容器是用來管理對象依賴關(guān)系的,對IoC容器來說,BeanDefinition就是對依賴反轉(zhuǎn)模式中管理的對象依賴關(guān)系的數(shù)據(jù)抽象,也是容器實現(xiàn)依賴反轉(zhuǎn)功能的核心數(shù)據(jù)結(jié)構(gòu),依賴反轉(zhuǎn)功能都是圍繞對這個BeanDefinition的處理來完成的。這些BeanDefinition就像是容器里裝的水,有了這些基本數(shù)據(jù),容器才能發(fā)揮作用。簡單一句話來說,BeanDefinition就是Bean的元數(shù)據(jù),BeanDefinition存放了對Bean的基本描述,包括Bean擁有什么屬性,方法,Bean的位置等等Bean的各種信息。IoC容器可以通過BeanDefinition生成Bean。

BeanDefinition究竟長什么樣呢?

在同第三章debug的地方一樣,點開beanFactory,然后查看beanDefinitionMap屬性。

image

OK,BeanDefinition就是長這樣了。具體怎么通過它生成Bean,在SpringBoot啟動流程中會詳細(xì)介紹。

四、SpringBoot web工程中的上下文 AnnotationConfigServletWebServerApplicationContext

在SpringBoot工程中,應(yīng)用類型分為三種,如下代碼所示。

public enum WebApplicationType {
    /**
     * 應(yīng)用程序不是web應(yīng)用,也不應(yīng)該用web服務(wù)器去啟動
     */
    NONE,
    /**
     * 應(yīng)用程序應(yīng)作為基于servlet的web應(yīng)用程序運(yùn)行,并應(yīng)啟動嵌入式servlet web(tomcat)服務(wù)器。
     */
    SERVLET,
    /**
     * 應(yīng)用程序應(yīng)作為 reactive web應(yīng)用程序運(yùn)行,并應(yīng)啟動嵌入式 reactive web服務(wù)器。
     */
    REACTIVE
}

對應(yīng)三種應(yīng)用類型,SpringBoot項目有三種對應(yīng)的應(yīng)用上下文,我們以web工程為例,即其上下文為AnnotationConfigServletWebServerApplicationContext

public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
        + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
        + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
        + "annotation.AnnotationConfigApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        } catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, "
                            + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

我們先看一下AnnotationConfigServletWebServerApplicationContext的設(shè)計。 
image

在2.2中已經(jīng)介紹了ApplicationContext的設(shè)計

關(guān)于AnnotationConfigServletWebServerApplicationContext詳細(xì)的設(shè)計路線在這里就不像DefaultListableBeanFactory容器那么詳細(xì)的去講解了。在第二章說過,應(yīng)用上下文可以理解成IoC容器的高級表現(xiàn)形式,拿上圖和DefaultListableBeanFactory的繼承關(guān)系圖,不難發(fā)現(xiàn),應(yīng)用上下文確實是在IoC容器的基礎(chǔ)上豐富了一些高級功能。在第二章中,我們還說過應(yīng)用上下文對IoC容器是持有的關(guān)系。繼續(xù)看第二章debug的截圖,context就是AnnotationConfigServletWebServerApplicationContext的神秘面孔,他的一個屬性beanFactory就是IoC容器(DefaultListableBeanFactory)。所以他們之間是持有,和擴(kuò)展的關(guān)系。

接下來看GenericApplicationContext類

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { 
     private final DefaultListableBeanFactory beanFactory; 
 ... 
}

第一行赫然定義了beanFactory屬性,正是DefaultListableBeanFactory對象。

關(guān)于上下文還有另外一個最重要的方法refresh,上文中說道該方法是在ConfigurableApplicationContext接口中定義的,那么在哪實現(xiàn)的該方法呢?

看AbstractApplicationContext類。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        //刷新上下文環(huán)境
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        //這里是在子類中啟動 refreshBeanFactory() 的地方
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        //準(zhǔn)備bean工廠,以便在此上下文中使用
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            //設(shè)置 beanFactory 的后置處理
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            //調(diào)用 BeanFactory 的后處理器,這些處理器是在Bean 定義中向容器注冊的
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            //注冊Bean的后處理器,在Bean創(chuàng)建過程中調(diào)用
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            //對上下文中的消息源進(jìn)行初始化
            initMessageSource();

            // Initialize event multicaster for this context.
            //初始化上下文中的事件機(jī)制
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            //初始化其他特殊的Bean
            onRefresh();

            // Check for listener beans and register them.
            //檢查監(jiān)聽Bean并且將這些監(jiān)聽Bean向容器注冊
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            //實例化所有的(non-lazy-init)單件
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            //發(fā)布容器事件,結(jié)束Refresh過程
            finishRefresh();
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        } finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

OK,應(yīng)用上下文就介紹到這里。

五、IoC容器的初始化過程

在這里我們先口述一下IoC容器的初始化過程吧,源碼分析,請移步SpringBoot啟動流程分析。

簡單來說IoC容器的初始化過程是由前面介紹的refresh()方法啟動的,這個方法標(biāo)志著IoC容器的正式啟動。具體來說,這個啟動包括三個過程

1 BeanDefinition的Resource定位
2 BeanDefinition的載入
3 向IoC容器注冊BeanDefinition

1、第一個過程:Resource定位

這個定位指的是BeanDefinition的資源定位,它由ResourceLoader通過統(tǒng)一的Resource接口完成,這個Resource對各種形式的BeanDefinition的使用都提供了統(tǒng)一接口。對于這些BeanDefinition的存在形式,可以是通過像SpringMVC中的xml定義的Bean,也可以是像在類路徑中的Bean定義信息,比如使用@Component等注解定義的。這個過程類似于容器尋找數(shù)據(jù)的過程,就像用水桶裝水先要把水找到一樣。

結(jié)合SpringBoot說一下這個過程,對于SpringBoot,我們都知道他的包掃描是從主類所在的包開始掃描的,那這個定位的過程在SpringBoot中具體是這樣的,在refresh容器之前(prepareContext()方法中),會先將主類解析成BeanDefinition,然后在refresh方法中并且是掃描Bean之前,解析主類的BeanDefinition獲取basePackage的路徑。這樣就完成了定位的過程。(先不討論SpringBoot中指定掃描包路徑和自動裝配)

2、第二個過程:BeanDefinition的載入

這個載入過程是把用戶定義好的Bean表示成IoC容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu),而這個容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu)就是BeanDefinition。

在SpringBoot中,上面我們說到通過主類找到了basePackage,SpringBoot會將該路徑拼接成:classpath:org/springframework/boot/demo//.class這樣的形式,然后一個叫做PathMatchingResourcePatternResolver的類會將該路徑下所有的.class文件都加載進(jìn)來,然后遍歷判斷是不是有@Component注解,如果有的話,就是我們要裝載的BeanDefinition。大致過程就是這樣的了。

注意:@Configuration,@Controller,@Service等注解底層都是@Component注解,只不過包裝了一層罷了。

3、第三個過程:注冊BeanDefinition

這個過程通過調(diào)用上文提到的BeanDefinitionRegister接口的實現(xiàn)來完成。這個注冊過程把載入過程中解析得到的BeanDefinition向IoC容器進(jìn)行注冊。通過上文的分析,我們可以看到,在IoC容器中將BeanDefinition注入到一個ConcurrentHashMap中,IoC容器就是通過這個HashMap來持有這些BeanDefinition數(shù)據(jù)的。比如DefaultListableBeanFactory 中的beanDefinitionMap屬性。

六、IoC容器的依賴注入

上面對IoC容器的初始化過程進(jìn)行了詳細(xì)的介紹,這個過程完成的主要的工作是在IoC容器中建立BeanDefinition數(shù)據(jù)映射。在此過程中并沒有看到IoC容器對Bean的依賴關(guān)系進(jìn)行注入。依賴注入是Spring實現(xiàn)“控制反轉(zhuǎn)”的重要一環(huán)。Spring將依賴關(guān)系交給IoC容器來完成。

依賴控制反轉(zhuǎn)的實現(xiàn)有很多種方式。在Spring中,IoC容器是實現(xiàn)這個模式的載體,它可以在對象生成或者初始化時直接將數(shù)據(jù)注入到對象中,也可以通過將對象注入到對象數(shù)據(jù)域中的方式來注入對方法調(diào)用的依賴。這種依賴注入是可以遞歸的,對象被逐層注入。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容