[Spring 系列] BeanFactory 與 ApplicationContext 詳解

本文主題

上篇文章 [重溫 IOC 設計理念] 後, 我想大家對 IOC 有了必定的瞭解。可是瞭解的同時,你確定也有帶着不少疑惑,例如說,
  1. Spring 中關於 IOC 的特定實現是什麼?
  2. 它們是以什麼方式來實現的?
  3. 若是 BeanFactory 和 ApplicationContext 都是容器的話,那麼它們究竟誰纔是底層 IOC 容器?還有它們面對真實的場景是什麼?

我以爲,從 IOC 這個概念引出來的疑惑是很是多的。因此,這篇文章我決定對那篇文章進行一個「坑」的填充。因此這篇文章講的主題是

IOC 在 Spring 的實現

或許不少人看過 Spring MVC 處理請求源碼。既然你看過,相信你會看到過一個類 DispatherServlet,它就是負責處理請求的核心類,至關於一箇中央調配器。而咱們能夠發現, DispatherServlet 有

一個構造器方法是注入一個 WebApplicationContext 的,代碼以下:

public DispatcherServlet(WebApplicationContext webApplicationContext) {    
        super(webApplicationContext);    
        this.setDispatchOptionsRequest(true);
}複製代碼
這個 WebApplicationContext 能夠理解爲 [ Web 環境的上下文 ]。學過計算機的同窗都應該知道,所謂上下文其實就是存儲一些運行時必要的數據。最爲經典的就是一個正在執行的線程若是被調度的話,CPU 是要將其一些上下文的數據進行保存例如說「執行到哪一行」「全局變量」等等。

那你可能會說,這個 WebApplicationContext 有什麼用呢?其實,WebApplicationContext 也是 IOC 的實現。若是你不相信,能夠看一下官網的定義:
定義連接: docs.spring.io/spring-fram… 咱們能夠看到如下定義:

The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory.

它說 BeanFactory 和 ApplicationCont

ext 皆爲 Spring IOC 容器。可是,前半句 Spring Doc 講了,BeanFactory 這個接口提供了先進的配置管理機制來管理。而 ApplicationContext 則是其子類。那麼,在講 ApplicationContext 以前咱們先講一下 BeanFactory。

BeanFactory

BeanFactory 翻譯過來就是 Bean 工廠。按照文檔來講,它有如下的特色
  1. BeanFactory 的 API 爲 Spring 的 IoC 功能提供了底層基礎。
  2. BeanFactory 遵循了「開閉原則」,對外開放擴展,對內關閉修改。它經過 BeanFactoryAware / InitializingBean / DisposableBean 來擴展其餘框架。
  3. BeanFactory 是核心 API,這也就意味着它功能很是簡潔,關注度高。BeanFactory 的實現不會對配置格式或要使用的任何組件註釋作任何假設。你能夠理解爲它不會要求你必定要遵照什麼規則或規範才能使用 Spring,由於它的抽象性,可兼容不一樣的實現風格例如 XmlBeanDefinitionReader 和 AutowiredAnnotationBeanPostProcessor 等等。因此這就是Spring的容器如此靈活和可擴展的本質。[若是面試官問你你能夠這麼回答哦!]

如今咱們來看看 BeanFactory 的一個基礎 DefaultListableBeanFactory 的繼承圖,能看出它究竟如何經過繼承的方式來實現以上特色。

API

看完了 BeanFactory,咱們來看看它的 API


觀察 API 能夠看出,我補充說明:
  1. BeanFactory 接口僅僅提供了讀功能卻沒有寫功能?其實這些寫功能會留給其子類進行實現的;
  2. 而後 BeanProvider 是在 Spring 中提供了延遲查找和注入的功能實現,之後有機會會講解說明
  3. 咱們能看到 BeanFactory 提供了 name 或者 requireType 來獲取 Bean。那是否是說明僅僅提供了兩種方法進行查找呢?這個就是咱們接下來要講的話題 - Bean 原元數據定義

Bean 原元數據定義

若想從 BeanFactory 獲取 Bean,那麼首先咱們須要講 Bean 的元數據搞進 Spring IOC 容器當中。怎麼弄?Spring 提供了兩種方法:
  1. 經過註解配置
  2. 經過 Java API 配置
  3. 經過 XML 配置

像註解配置,咱們常常會看到 Spring Boot 使用了 @Autowire 註解,例如說

public class UserService {
    @Autowireprivate 
    UserDao userDao;
}複製代碼

像 Java 配置,咱們也可使用 @Configuration / @Bean / @Import / @DependsOn,例如說html

@Configuration
public class SecurityConfig{
    @Bean public UserDao userDao() {
        return new UserDao();
    }
}複製代碼
像 XML 配置就不用說了,那是 Spring / Hibernate / Structs 2 時代進行作的事情,例如說

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>
</beans>複製代碼


上面是我總結了一下其 Bean 元數據初始化的方法。或許你會問我究竟這幾種元數據初始化方法,誰勝誰劣?目前按照個人認知來講若是單從便利性來比較是不可取的,我只能說各類方式都 not bad。各有個的應用場景,像 Spring Boot 這種約定大於配置的框架來講,更傾向於註解的方式,可是不意味着 XML 被淘汰了。由於 XML 的方式在某些特定場景仍是會被用上,又或者是爲了兼容老項目,Spring Boot 還特地保留着。因此,即便是多種選擇,在業務架構上可能多種選擇結合纔是最佳方案。

看完了容器元數據的初始化,BeanFactory 做爲 Spring IOC 最基礎的容器,大部分時候咱們不會直接從 BeanFactory 獲取 Bean 的,那怎麼使用它獲取 Bean 呢?那接下來就要引出了其優秀的子類 - ApplicationContext 了!

ApplicationContext

上面已經說了 ApplicationContext 是 BeanFactory 的子類。如今我開始完善一下信息。ApplicationContext 是位於 org.springframework.context 包下。它屬於 BeanFactory 的擴展加強。ApplicationContext 擴展都是一些面向框架的功能,例如說
  1. i18n 國際化
  2. 有強大的資源抽象類 Resource 和加載起 ResourceLoader,能夠讀取 URLS 和 文件
  3. 事件監聽機制
  4. 能夠加載多個具備層級關係的上下文

從上面的選項其實咱們均可以看到,Spring 框架考慮都是一些很經常使用的,圍繞面向企業級別應用的特性。你想一想,一個擁有諸多特性,並且又擁有 IOC 容器的功能類,那確定就像是一個框架內部的控制器,方便別的組件進行調用。因此這也是爲何咱們通常不直接經過 BeanFactory 進行 Bean 的獲取,這也算是 「外開內聚」
的體現吧。那接下來咱們看一下怎麼經過 ApplicationContext 獲取 Bean

假設咱們在 resources/META-INF 目錄下有個配置文件 servicees.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> 

    <bean id="userDao" class="com.jc.test01.dao.UserDao"> 
    </bean> 

</beans>複製代碼
而後咱們使用 Java API 形式進行獲取

// create and configure beans	建立和配置 BeansApplicationContext context = new ClassPathXmlApplicationContext("services.xml");

// retrieve configured instance  經過 名稱+類型進行查找
UserDao userDao = context.getBean("userDao", UserDao.class);

// use configured instance	調用方法
List<String> userList = userDao.getUsernameList();複製代碼

上面的代碼我要解釋一下。ClassPathXmlApplicationContext 是 ApplicationContext 的擴展,就好像上面說的,Spring 多數狀況下都是經過其豐富的子類來實現多種不一樣的應用風格。例如 ClassPathXmlApplicationContext 從名字看來咱們能夠知道它的意思是 「從 classPath 加載 XML 文件而來的上下文」[說明這是一個面向 XML 風格的實現類]。相似的還有註解風格的 AnnotationConfigApplicationContext ,還有文件系統風格的 FileSystemXmlApplicationContext 的等等。有興趣能夠經過 IDEA 來調試其繼承圖,你會大有收穫。

繼承圖



在圖上的粉紅色圈圈,咱們能夠看到 ApplicationContext 的擴展特性實現的接口。總的來講,Spring 更喜歡的是經過組合繼承的方法來實現特性,這樣使各個類具有更高的專一度,提升封裝和利用率。

結語

到了文章末尾,我要回答這幾個問題。

Spring 中關於 IOC 的特定實現是什麼?
特定的實現是 BeanFactory 和 ApplicationContext。

它們是以什麼方式來實現的?
其實從它們的類圖就能夠發現,它們其實都會經過組合和繼承。如上面所說的,這樣的好處在於提升各個類的職能,使其更加專一於其技能,也便於外部進行擴展。

若是 BeanFactory 和 ApplicationContext 都是容器的話,那麼它們究竟誰纔是底層 IOC 容器?還有它們面對真實的場景是什麼?
上面說了 BeanFactory 和 ApplicationContext 皆爲 IOC 的實現。可是 BeanFactory 是專一於提供最核心最基礎的 IOC 功能,而 ApplicationContext 是面向企業級別應用而集成更多便於開發的特性的 IOC 實現。它們兩個專一方向不一樣,非多餘實現,互不影響。
相關文章
相關標籤/搜索