Spring(二)核心容器 - 簡(jiǎn)介 、BeanFactory、ApplicationContext

前言

在上篇文章中,和大家一起討論了 Spring 的整體架構(gòu),其大致分為五個(gè)模塊:核心容器、AOP、Web、Data 數(shù)據(jù)訪問、Test模塊。其中核心容器是 Spring 的核心部分,其它模塊也都依賴于該容器。這里和就大家一起深入討論 Spring 的容器,它的作用是什么、怎么實(shí)現(xiàn)的。

1、容器簡(jiǎn)介

容器顧名思義就是用來裝東西的,裝的是什么?裝的是 Bean。

Bean 是 Spring 的基本單位,在基于 Spring 的 Java EE 應(yīng)用中,所有的組件都被當(dāng)成 Bean 處理,包括數(shù)據(jù)源、Hibernate 的 SessionFactory、事務(wù)管理器等。在 Spring 中,Bean 是一個(gè)非常廣義的概念,任何 Java 對(duì)象、Java 組件都被當(dāng)成 Bean 處理。

那容器僅僅是用來保存 Bean 這么簡(jiǎn)單么?不是。

當(dāng)我們需要使用某個(gè) Bean 時(shí),容器會(huì)自動(dòng)幫我們創(chuàng)建,并在適當(dāng)時(shí)銷毀。還有一種情況,當(dāng)某個(gè) Bean 中需創(chuàng)建另一個(gè) Bean 時(shí),也就是 Bean 之間有依賴關(guān)系,這種依賴的 Bean 也是由容器自動(dòng)創(chuàng)建。在外界有一個(gè)標(biāo)準(zhǔn)的名詞,前者稱呼為 IOC,也就是控制反轉(zhuǎn),后者稱呼為 DI,也就是依賴注入。

IOC/DI

IOC (Inversion of Control) 控制反轉(zhuǎn):所謂控制反轉(zhuǎn),就是當(dāng)我們需要某個(gè) Bean 時(shí),將 Bean 的名稱告知容器,由容器去創(chuàng)建該 Bean,而不是我們手動(dòng) new 一個(gè),這里 Bean 創(chuàng)建管理的控制權(quán)都交給了容器,所以這是一種控制權(quán)的反轉(zhuǎn)。其通俗點(diǎn)講就是需要什么東西讓別人送過來,而不是自己去拿。

DI (Dependency Injection) 依賴注入:就是指當(dāng) A Bean 里面需創(chuàng)建 B Bean 時(shí),會(huì)在創(chuàng)建 A Bean 的時(shí)候,自動(dòng)將依賴的 B Bean 注入進(jìn)去,其 B Bean 是被動(dòng)接受注入而不是自己主動(dòng)去找。換句話說就是指 A Bean 不是從容器中查找它依賴的 B Bean,而是在容器創(chuàng)建 A Bean 候主動(dòng)將它依賴的 B Bean 注入給它。

IOC 和 DI 其實(shí)歸根結(jié)底實(shí)現(xiàn)的功能是相同的,只是同樣的功能站在不同的角度來闡述罷了,不過我們通常喜歡將這兩個(gè)概念統(tǒng)稱為 IOC。當(dāng)然,在真實(shí)場(chǎng)景中,交由 Spring 容器創(chuàng)建的 Bean 泛指在應(yīng)用程序中的表現(xiàn)層、業(yè)務(wù)層、持久層等各層對(duì)應(yīng)的 Bean,如 Controller、Service 等;進(jìn)行數(shù)據(jù)交互的模型,如 DTO、VO 等就不需交由 Spring 來創(chuàng)建。

所以,容器本質(zhì)上可以也可以看作是 Bean 工廠,該工廠管理 Bean 的生命周期,以及 Bean 之間的依賴關(guān)系。外界也將 Spring 容器稱為 IOC 容器。當(dāng)然,這里容器僅僅是 Spring 的抽象概念,代碼中將其具象化為 BeanFactory 或 ApplicationContext,容器功能也由具象化的類進(jìn)行處理。

2、容器的結(jié)構(gòu)

容器的實(shí)現(xiàn)類并不是唯一的,Spring 框架提供了多個(gè)容器的實(shí)現(xiàn),這些容器分為兩套體系:一套是早期的 BeanFactory 體系;還有一套是現(xiàn)在常用的 ApplicationContext,也可稱為應(yīng)用上下文,它繼承了 BeanFactory,它除了有 BeanFactory 的功能外
,還提供了其他服務(wù),例如事務(wù)和 AOP 服務(wù)、國(guó)際化(il8n)的消息源以及應(yīng)用程序事件處理等企業(yè)級(jí)的服務(wù)。

說到這,就不得不說 Spring 的兩種配置方式,在早期都是 XML 配置文件的方式,而現(xiàn)在使用的是注解配置的方式。BeanFactory 體系的容器一般用來處理 XML 配置文件的方式,而 ApplicationContext 體系則都可以處理。

2.1 BeanFactory

BeanFactory 是容器最基礎(chǔ)的類,它定義了容器的基本功能規(guī)范:

public interface BeanFactory {

    // 對(duì) FactoryBean 的轉(zhuǎn)義定義,因?yàn)槿绻褂?bean 的名字檢索 FactoryBean 得到的對(duì)象是工廠生成的對(duì)象,
    // 如果需要得到工廠本身,需要轉(zhuǎn)義(FactoryBean 在后續(xù)會(huì)詳細(xì)介紹)
    String FACTORY_BEAN_PREFIX = "&";
    
    // 根據(jù) bean 的名字,獲取在容器中 bean 實(shí)例
    Object getBean(String name) throws BeansException;
    
    //根據(jù) bean 的名字和 Class 類型來得到 bean 實(shí)例,增加了類型安全驗(yàn)證機(jī)制。
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    
    // 提供對(duì) bean 的檢索,看看是否在容器有這個(gè)名字的 bean
    boolean containsBean(String name);
    
    // 根據(jù) bean 名字,判斷這個(gè) bean 是不是單例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    // 根據(jù) bean 名字,判斷這個(gè) bean 是不是原型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    
    // 根據(jù) bean 名字,判斷是否與指定的類型匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
    // 得到 bean 實(shí)例的 Class 類型
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    // 得到bean 的別名,如果根據(jù)別名檢索,那么其原名也會(huì)被檢索出來
    String[] getAliases(String name);
}

在 BeanFactory 里只對(duì)容器的基本行為作了定義,其根本不關(guān)心你的 Bean 是如何定義怎樣加載的。
正如我們只關(guān)心工廠里得到什么的產(chǎn)品對(duì)象,至于工廠是怎么生產(chǎn)這些對(duì)象的,這個(gè)基本的接口不關(guān)心。而要知道工廠是如何產(chǎn)生對(duì)象的,我們就需要看具體的容器了,也就是 BeanFactory 的子類。

BeanFactory 體系中常用的實(shí)現(xiàn)類有:

  • ListableBeanFactory:提供容器中 bean 迭代的功能。如返回所有 Bean 的名字、容器中 Bean 的數(shù)量等。
  • HierarchicalBeanFactory:提供父容器的訪問功能,可通過 ConfigurableBeanFactory 的 setParentBeanFactory 方法設(shè)置父容器。
  • AutowireCapableBeanFactory:為 Spring 容器之外的 Bean ,也就是未交由 Spring 管理的 Bean ,提供依賴注入的功能。

以上三個(gè)是 BeanFactory 的直系親屬,這個(gè)三個(gè)直系親屬下面又派生了兩個(gè)復(fù)雜的容器:

  • ConfigurableBeanFactory:其繼承了 HierarchicalBeanFactory 和 SingletonBeanRegistry 這兩個(gè)接口,其提供了很多方法,如:定義類加載器、類型轉(zhuǎn)化、屬性編輯器、注冊(cè)依賴 Bean 、銷毀 bean 等,且該接口被大多數(shù)的容器繼承、實(shí)現(xiàn)。
  • ConfigurableListableBeanFactory:這個(gè)接口繼承了 ListableBeanFactory、 AutowireCapableBeanFactory、ConfigurableBeanFactory,自身主要提供用于分析和修改 bean 定義以及預(yù)先實(shí)例化單例 Bean 的方法。

最后是核心容器:

  • DefaultListableBeanFactory:它實(shí)現(xiàn)了以上所有的接口,在 BeanFactory 體系中可以作為一個(gè)獨(dú)立的容器使用。

BeanFactory 大致的繼承關(guān)系如下:

BeanFactory 結(jié)構(gòu)

其實(shí)以前常用的容器是 XmlBeanFactory ,它是 DefaultListableBeanFactory 的實(shí)現(xiàn)類,現(xiàn)已被廢除,原因還未找到,有知道的小伙伴,可在底下留言告知。

但我們基本不單獨(dú)使用 BeanFactory ,而是直接使用 ApplicationContext ,因?yàn)?ApplicationContext 包含了 BeanFactory。

2.2 ApplicationContext

上面說過 ApplicationContext 是 BeanFactory 子類,它不僅包含 BeanFactory 所有功能,還對(duì)其進(jìn)行了擴(kuò)展,而我們喜歡將 ApplicationContext 稱為應(yīng)用上下文,因?yàn)槿萜髦皇撬幕竟δ堋?/p>

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

    // 返回此應(yīng)用程序上下文的唯一ID
    @Nullable
    String getId();

    // 返回此上下文所屬的應(yīng)用程序名稱
    String getApplicationName();

    // 返回應(yīng)用上下文具像化的類名
    String getDisplayName();

    // 返回第一次加載此上下文時(shí)的時(shí)間戳
    long getStartupDate();

    // 獲取父級(jí)應(yīng)用上下文
    @Nullable
    ApplicationContext getParent();

    // 將 AutowireCapableBeanFactory 接口暴露給外部使用
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

ApplicationContext 自身提供的方法非常簡(jiǎn)單,但它繼承了六個(gè)接口,來擴(kuò)展自身功能:

  • EnvironmentCapable:獲取 Environment。
  • ListableBeanFactory、HierarchicalBeanFactory:這是 BeanFactory 體系接口,分別提供 Bean 迭代和訪問父容器的功能。
  • MessageSource:支持國(guó)際化功能。
  • ApplicationEventPublisher:應(yīng)用事件發(fā)布器,封裝事件發(fā)布功能的接口。
  • ResourcePatternResolver:該接口繼承至 ResourceLoader ,作用是加載多個(gè) Resource。

ApplicationContext 同樣提供了非常多的實(shí)現(xiàn)類,其又可細(xì)分為兩大類, ConfigurableApplicationContext 和 WebApplicationContext。

2.2.1 ConfigurableApplicationContext

該接口是比較重要的一個(gè)接口,幾乎所有的應(yīng)用上下文都實(shí)現(xiàn)了該接口。該接口在ApplicationContext的基礎(chǔ)上提供了配置應(yīng)用上下文的能力,此外提供了生命周期的控制能力。

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {

    // 應(yīng)用上下文配置時(shí),這些符號(hào)用于分割多個(gè)配置路徑
    String CONFIG_LOCATION_DELIMITERS = ",; \t\n";

    // BeanFactory中,ConversionService類所對(duì)應(yīng)的bean的名字。如果沒有此類的實(shí)例的話嗎,則使用默認(rèn)的轉(zhuǎn)換規(guī)則
    String CONVERSION_SERVICE_BEAN_NAME = "conversionService";

    //LoadTimeWaver類所對(duì)應(yīng)的Bean在容器中的名字。如果提供了該實(shí)例,上下文會(huì)使用臨時(shí)的 ClassLoader ,這樣,LoadTimeWaver就可以使用bean確切的類型了 
    String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";

    // Environment 類在容器中實(shí)例的名字
    String ENVIRONMENT_BEAN_NAME = "environment";

    // System 系統(tǒng)變量在容器中對(duì)應(yīng)的Bean的名字
    String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";

    // System 環(huán)境變量在容器中對(duì)應(yīng)的Bean的名字
    String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";

    // 設(shè)置容器的唯一ID
    void setId(String id);

    // 設(shè)置此容器的父容器
    void setParent(@Nullable ApplicationContext parent);

    // 設(shè)置容器的 Environment 變量
    void setEnvironment(ConfigurableEnvironment environment);

    // 以 ConfigurableEnvironment 的形式返回此容器的環(huán)境變量。以使用戶更好的進(jìn)行配置
    @Override
    ConfigurableEnvironment getEnvironment();

    // 此方法一般在讀取應(yīng)用上下文配置的時(shí)候調(diào)用,用以向此容器中增加BeanFactoryPostProcessor。增加的Processor會(huì)在容器refresh的時(shí)候使用。
    void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);

    // 向容器增加一個(gè) ApplicationListener,增加的 Listener 用于發(fā)布上下文事件,如 refresh 和 shutdown 等
    void addApplicationListener(ApplicationListener<?> listener);

    // 向容器中注入給定的 Protocol resolver
    void addProtocolResolver(ProtocolResolver resolver);

    // 這是初始化方法,因此如果調(diào)用此方法失敗的情況下,要將其已經(jīng)創(chuàng)建的 Bean 銷毀。
    // 換句話說,調(diào)用此方法以后,要么所有的Bean都實(shí)例化好了,要么就一個(gè)都沒有實(shí)例化
    void refresh() throws BeansException, IllegalStateException;

    // 向JVM注冊(cè)一個(gè)回調(diào)函數(shù),用以在JVM關(guān)閉時(shí),銷毀此應(yīng)用上下文
    void registerShutdownHook();

    // 關(guān)閉此應(yīng)用上下文,釋放其所占有的所有資源和鎖。并銷毀其所有創(chuàng)建好的 singleton Beans
    @Override
    void close();

    // 檢測(cè)此 FactoryBean 是否被啟動(dòng)過
    boolean isActive();

    // 返回此應(yīng)用上下文的容器。
    // 千萬不要使用此方法來對(duì) BeanFactory 生成的 Bean 做后置處理,因?yàn)閱卫?Bean 在此之前已經(jīng)生成。
    // 這種情況下應(yīng)該使用 BeanFactoryPostProcessor 來在 Bean 生成之前對(duì)其進(jìn)行處理
    ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}

該接口下又有幾個(gè)重要的實(shí)現(xiàn)類:

  • AbstractApplicationContext:這是個(gè)抽象類,僅實(shí)現(xiàn)了公共的上下文特性。這個(gè)抽象類使用了模板方法設(shè)計(jì)模式,需要具體的實(shí)現(xiàn)類去實(shí)現(xiàn)這些抽象的方法。
  • GenericApplicationContext:該類繼承自 AbstractApplicationContext,是為通用目的設(shè)計(jì)的,它能加載各種配置文件,例如 xml,properties 等等。它的內(nèi)部持有一個(gè) DefaultListableBeanFactory 的實(shí)例,實(shí)現(xiàn)了 BeanDefinitionRegistry 接口,以便允許向其應(yīng)用任何 bean 的定義的讀取器。
  • AnnotationConfigApplicationContext:該類繼承自 GenericApplicationContext ,提供了注解配置(例如:@Configuration、@Component等)和類路徑掃描(scan方法)的支持。

2.2.2 WebApplicationContext

該接口是專門為 Web 應(yīng)用準(zhǔn)備的,其允許從相對(duì)于 Web 根目錄的路徑中裝載配置文件完成初始化。

public interface WebApplicationContext extends ApplicationContext {

    // 整個(gè) Web 應(yīng)用上下文是作為屬性放置在 ServletContext 中的,該常量就是應(yīng)用上下文在 ServletContext 屬性列表中的 key
    String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

    // 定義了三個(gè)作用域的名稱
    String SCOPE_REQUEST = "request";
    String SCOPE_SESSION = "session";
    String SCOPE_APPLICATION = "application";

    // 在工廠中的 bean 名稱
    String SERVLET_CONTEXT_BEAN_NAME = "servletContext";

    // ServletContext 初始化參數(shù)名稱
    String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";

    // 在工廠中 ServletContext 屬性值環(huán)境bean的名稱
    String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";

    // 用來獲取 ServletContext 對(duì)象
    @Nullable
    ServletContext getServletContext();
}

該接口的核心實(shí)現(xiàn)類有:

  • ConfigurableWebApplicationContext:該接口同時(shí)繼承了 WebApplicationContext 和 ConfigurableApplicationContext,提供了 Web 應(yīng)用上下文的可配置的能力。
  • GenericWebApplicationContext:該類繼承自 GenericApplicationContext,實(shí)現(xiàn)了 ConfigurableWebApplicationContext。
  • XmlWebApplicationContext:該上下文是使用 Xml 配置文件的方式,不過是在 Web 環(huán)境中使用的。
  • AnnotationConfigServletWebServerApplicationContext:該類是被 SpringBoot 擴(kuò)展而來的,SpringBoot 使用的就是該上下文。

2.3 差異對(duì)比

從上面可以看出 BeanFactory 是 Sping 框架的基礎(chǔ)接口,一般是面向 Spring 本身;而 ApplicationContext 是以 BeanFactory 為基礎(chǔ)進(jìn)行綜合能力擴(kuò)展,用于滿足大型業(yè)務(wù)應(yīng)用的創(chuàng)建, ApplicationContext 一般面向使用 Sping 框架的開發(fā)者。幾乎所有的應(yīng)用場(chǎng)合我們都是直接使用 ApplicationContet 而非底層的 BeanFactory。

下表列出了BeanFactory 和 ApplicationContext 接口和實(shí)現(xiàn)所提供的功能:

功能 / 特點(diǎn) BeanFactory ApplicationContext
Bean 實(shí)例化/裝配
BeanPostProcessor 自動(dòng)注冊(cè) 沒有
BeanFactoryPostProcessor 自動(dòng)注冊(cè) 沒有
MessageSource 便捷訪問(針對(duì)i18n) 沒有
ApplicationEvent 發(fā)布 沒有

兩者還有一個(gè)區(qū)別是:

  • ApplicationContext 在容器啟動(dòng)時(shí),一次性創(chuàng)建了所有的 Bean。
  • BeanFactory 在容器啟動(dòng)時(shí),并未創(chuàng)建 Bean,直到第一次訪問某個(gè) Bean 時(shí)才創(chuàng)建目標(biāo) Bean。

3、ApplicationContext 準(zhǔn)備啟動(dòng)

在真實(shí)環(huán)境中,一般通過集成 SSM 或者 SpringBoot 來自動(dòng)創(chuàng)建 ApplicationContext。

先從 SSM 開始

1、在 web.xml 配置監(jiān)聽器

<!-- spring監(jiān)聽器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

2、容器啟動(dòng)時(shí)會(huì)調(diào)用 ContextLoaderListener 中的 contextInitialized 方法。

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    
    ...
    
    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }
    
    ...
}

3、調(diào)用父類的 initWebApplicationContext 方法,在該方法中創(chuàng)建、啟動(dòng)上下文。

public class ContextLoader {
    
    ...
    
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        
        ...

        try {
            
            if (this.context == null) {
            
                // 通過 createWebApplicationContext 方法創(chuàng)建上下文,默認(rèn)創(chuàng)建 XmlWebApplicationContext
                this.context = createWebApplicationContext(servletContext);
            }
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                if (!cwac.isActive()) {
                    
                    ...
                    // 在該方法中調(diào)用上下文的 refresh 方法,refresh 就是啟動(dòng)上下文的入口
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }
            ...
        }
        ...
    }
    ...
    
    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        
        ... 
        
        wac.refresh();
    }
    
    ...
}

SpringBoot 啟動(dòng) ApplicationContext

1、從啟動(dòng)類開始

@SpringBootApplication
public class DiveInSpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(DiveInSpringBootApplication.class, args);
    }
}

2、找到 SpringApplication 中,最后重載的 run 方法

public ConfigurableApplicationContext run(String... args) {
    
    ...
    
    ConfigurableApplicationContext context = null;
    
    ...
    
    try {
        ...
        // 通過 createApplicationContext 方法創(chuàng)建上下文,根據(jù) Web 環(huán)境不同創(chuàng)建的上下文也不同
        context = createApplicationContext();
    
        ...
        // 該方法用于啟動(dòng)上下文
        refreshContext(context);
        ...
    
    }
    catch (Throwable ex) {
        ...
    }
    
    context = createApplicationContext();
    
    ...
}

3、進(jìn)入 refreshContext 方法,里面調(diào)用了 refresh 方法

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

4、這里,最終也是調(diào)用 ApplicationContext 的 refresh 方法來啟動(dòng)上下文

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

注:這里主要討論容器的創(chuàng)建和啟動(dòng)部分,所以省略了其他部分的代碼。其中 SpringBoot 啟動(dòng)上下文在前幾篇 《SpringBoot系列》文章有詳細(xì)介紹,感興趣的伙伴可自行查閱

可以看到雖然 SSM 和 SpringBoot 的上下文對(duì)象不同,但最終都是調(diào)用上下文中的 refresh 方法來啟動(dòng)。該方法是 ApplicationContext 的核心,如 Bean 注冊(cè)、注入、解析 XML 、解析注解等是從該方法開始,其內(nèi)部實(shí)現(xiàn)大致如下:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 初始化 refresh 的上下文環(huán)境,就是記錄下容器的啟動(dòng)時(shí)間、標(biāo)記已啟動(dòng)狀態(tài)、處理配置文件中的占位符
        prepareRefresh();

        // 2. 初始化 BeanFactory,加載并解析配置
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

        /* ---至此,已經(jīng)完成了簡(jiǎn)單容器的所有功能,下面開始對(duì)簡(jiǎn)單容器進(jìn)行增強(qiáng)--- */

        // 3. 對(duì) BeanFactory 進(jìn)行功能增強(qiáng),如設(shè)置BeanFactory的類加載器,添加幾個(gè) BeanPostProcessor,手動(dòng)注冊(cè)幾個(gè)特殊的 bean
        prepareBeanFactory(beanFactory);

        try {
            // 4. 后置處理 beanFactory,交由子類實(shí)現(xiàn)
            postProcessBeanFactory(beanFactory);

            // 5. 調(diào)用已注冊(cè)的 BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);

            // 6. 注冊(cè) BeanPostProcessor,僅僅是注冊(cè),調(diào)用在getBean的時(shí)候
            registerBeanPostProcessors(beanFactory);

            // 7. 初始化國(guó)際化資源
            initMessageSource();

            // 8. 初始化事件廣播器
            initApplicationEventMulticaster();

            // 9. 留給子類實(shí)現(xiàn)的模板方法
            onRefresh();

            // 10. 注冊(cè)事件監(jiān)聽器
            registerListeners();

            // 11. 實(shí)例化所有非延遲加載的單例
            finishBeanFactoryInitialization(beanFactory);

            // 12. 完成刷新過程,發(fā)布應(yīng)用事件
            finishRefresh();
            
        } catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
            }
            
            // 13.銷毀已經(jīng)初始化的 singleton 的 Beans,以免有些 bean 會(huì)一直占用資源
            this.destroyBeans();
            
            // Reset 'active' flag.
            this.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...
            this.resetCommonCaches();
        }
    }
}

接下來的文章,也是對(duì) refresh 方法中的各部分進(jìn)行詳細(xì)討論。

4、總結(jié)

最后來做個(gè)整體的總結(jié)。文章從 Spring 的整體架構(gòu)開始討論,整體分為五個(gè)模塊:核心容器、AOP、Web、Data 數(shù)據(jù)訪問、Test模塊,而核心容器又是 Spring 的基礎(chǔ)。容器的作用是什么?提供 IOC/DI 功能;怎么實(shí)現(xiàn)的?其核心是 BeanFactory 和 ApplicationContext ,一般使用 ApplicationContext ,其包含了 BeanFactory 的所有功能,并對(duì)其進(jìn)行擴(kuò)展。在 SSM 和 SpringBoot 中自動(dòng)創(chuàng)建 ApplicationContext 并調(diào)用它的 refresh 方法進(jìn)行啟動(dòng),它的 refresh 就是實(shí)現(xiàn)容器一系列功能的入口。



以上就是本章內(nèi)容,如果文章中有錯(cuò)誤或者需要補(bǔ)充的請(qǐng)及時(shí)提出,本人感激不盡。

參考:

https://www.cnblogs.com/09120912zhang/p/7746252.html
https://docs.spring.io/spring/docs/5.2.3.RELEASE/spring-framework-reference/core.html#context-introduction
http://www.lxweimin.com/p/2854d8984dfc
https://blog.csdn.net/baidu_36327010/article/details/87983262
https://www.cnblogs.com/zhangfengxian/p/11192054.html
https://www.cnblogs.com/sharpest/p/10885820.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,566評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,055評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,303評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,683評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,899評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,135評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評(píng)論 1 282
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,528評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,844評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容