如何實現一個簡易版的 Spring - 如何實現 Setter 注入
阿新 • • 發佈:2020-11-29
#### 前言
之前在 [上篇](https://www.mghio.cn/post/558ca0bd.html) 提到過會實現一個簡易版的 IoC 和 AOP,今天它終於來了。。。相信對於使用 Java 開發語言的朋友們都使用過或者聽說過 Spring 這個開發框架,絕大部分的企業級開發中都離不開它,通過 [官網](https://spring.io) 可以瞭解到其生態非常龐大,針對不同方面的開發提供了一些解決方案,可以說 Spring 框架的誕生是對 Java 開發人員的一大福利,自 2004 年釋出以來,Spring 為了解決一些企業開發中的痛點先後引入了很多的特性和功能,其中最重要的就是我們經常聽到的 IoC 和 AOP 特性,由於涉及到的知識和細節比較多,會分為幾篇文章來介紹,今天這篇(也是第一篇)我們來看看如何實現基於 XML 配置方式的 **Setter 注入**。
#### 預備知識
既然是通過 XML 配置檔案的方式,首先第一件事就是要讀取 XML 檔案然後轉換為我們需要的資料結構,解析 XML 檔案有但不限於這些方式([JDOM](http://www.jdom.org)、[XOM](http://www.xom.nu)、[dom4j](https://dom4j.github.io)),這裡使用的是簡單易上手的 [dom4j](https://dom4j.github.io),所你得對其基礎知識有一些簡單瞭解,其實都是一些很簡單的方法基礎使用而已,第二個就是你要有一些 Spring 框架的使用經驗,這裡實現的簡易版本質上是對 Spring 的一個精簡後的核心部分的簡單實現,是的,沒錯,你只需要有了這些基礎預備知識就可以了。
#### 基礎資料結構抽象
在開始編碼實現前先要做一些簡單的構思和設計,首先在 Spring 中把一個被其管理的物件稱之為 Bean,然後其它的操作都是圍繞這個 Bean 來展開設計的,所以為了能在程式中統一併且規範的表示一個 Bean 的定義,於是第一個介面 BeanDefinition 就出來了,本次需要的一些基本資訊包含 Bean 的名稱、所屬類名稱、是否單例、作用域等,如下所示:
![spring-injection-beandefinition-1.png](https://i.loli.net/2020/11/28/1UGMFCPVfwgl3D7.png)
現在 BeanDefinition 有了,接下來就是要根據這個 BeanDefinition 去創建出對應的 Bean 例項了,很顯然這需要一個 Factory 工廠介面去完成這個建立的工作,這個建立 Bean 的介面命名為 BeanFactory,其提供根據不同條件去建立相對應的 Bean 例項功能(比如 beanId),但是建立的前提是需要先註冊這個 BeanDefinition,然後根據一定條件再從中去獲取 BeanDefinition,根據 [單一職責](https://en.wikipedia.org/wiki/Single-responsibility_principle) 原則,這個功能應該由一個新的介面去完成,主要是做註冊和獲取 BeanDefinition 的工作,故將其命名為 BeanDefinitionRegistry,我們需要的 BeanDefinition 要從哪裡獲取呢?很顯然我們是基於 XML 配置的方式,當然是從 XML 配置檔案中獲取到的,同樣根據單一職責原則,也需要一個類去完成這個事情,將其命名為 XMLBeanDefinitionReader,這部分的整體結構如下所示:
![spring-injection-beanfactory-2.png](https://i.loli.net/2020/11/28/YnDv6MqHVNwTAg5.png)
接下來面臨的一個問題就是,像 XML 這種配置檔案資源要如何表示呢,這些配置對於程式來說是一種資源,可以統一抽象為 Resource,然後提供一個返回資源對應流(InputStream)物件介面,這種資源可以從專案中獲取、本地檔案獲取甚至是從遠端獲取,它們都是一種 Resource,結構如下:
![spring-injection-resource-3.png](https://i.loli.net/2020/11/29/GBP2M3aZLhbfcm5.png)
最後就是要一個提供去組合呼叫上面的那些類去完成 XML 配置檔案解析為 BeanDefinition 並注入到容器中了的功能,擔任這程式上下文的職責,將其命名為 ApplicationContext,這裡同樣也可以根據 Resource 的型別分為多種不同的類,比如:FileSystmXmlApplicationContext、ClassPathXmlApplicationContext 等,這些內部都有一個將配置檔案轉換為 Resource 的過程,可以使用 [模板方法](https://en.wikipedia.org/wiki/Template_method_pattern) 抽象出一個公共父類抽象類,如下所示:
![spring-injection-applicationcontext.png](https://i.loli.net/2020/11/29/ux1cEOAGQoqD9h8.png)
總結以上分析結果,得出初步類圖設計如下:
![spring-injection-all-4.png](https://i.loli.net/2020/11/29/wOijZNT8cAlMY1r.png)
最終要實現 Setter 注入這個目標,可以將其分解為以下兩個步驟:
1. 將 XML 配置文