讓Spring在你面前裸奔(一)

開篇

一說到Spring Ioc,我們很多小夥伴很本能的想到了在開發時候,我們在一個類上加上諸如@Component之類的註解,然後再在另外一個同樣加着註解的類中用@Autowired之類的註解去引用就好了。那麼這樣子編程有什麼好處呢?我們一起看看下面的代碼,注意看註釋部分

image
image

BService,和CService的代碼和AService的代碼一樣,綠色下劃線的不用管,那是作者自己配置的sonarLint在提示field從未被使用過。再看看有了Spring IOC的吧,雖然大家都知道,但是我還是貼出來:

image

其他的類也就加上了@Component註解而已,代碼很簡單,大家應該一下就能看明白,引入Ioc之後,我們不再需要關心對象和對象之間的依賴關係,開發的時候只需要在需要的類上標上註解,然後使用的時候用註解引用即可。我們稱這種好處爲解耦,即不再需要關注對象和對象之間耦合的關係。同時,我們的代碼也變得簡潔很多,比如上面的圖中,doSomeThing這個方法只需要關注自己的業務邏輯即可,無需再關注複雜的對象創建過程。

很多人依然不知道這個不需要關注對象和對象之間耦合關係意味着什麼。面向對象編程的痛點其實就是對象和對象之間的耦合關係,在早些時候的大型單體項目裏面,類的擴展往往是通過設計模式來搞,但是引入了設計模式之後,類和類的關係變得複雜,這導致了別人在用的時候,需要先理清楚這個類和它所依賴的類之間的關係,然後纔可以去創建對象,而且一旦依賴關係發生了改變,很多地方其他人使用你這玩意的代碼也要變,這就是個很悲劇的事情。當然,也不一定是因爲設計模式導致類和類關係變的複雜,也可能是別的原因。現在因爲人們都在玩微服務了,業務類之間的依賴關係由於服務拆分的原因,一般都不會太複雜。

但是這背後的,Spring爲我們做了什麼呢?這是一個很複雜的流程,也是本系列文章要爲小夥伴們講清楚的東西,除此之外,還有在這個加載過程中一些可以擴展的點,爭取能讓大家對spring有更深入的認識。

本系列文章是基於spring 4.0版本,本文中所使用的源碼在這裏https://github.com/BillBillLi/spring-framework-study.

這是我fork的spring-framework的項目,只不過在裏面都加上了我自己的註釋,如果看文章裏的代碼塊看不明白,你可以把它clone到本地,然後對着裏面的代碼來看,當然你應該切換到4.0.x的分支上。

在開始之前,我們先來看一段熟悉的代碼:

image

application-context文件內容如下:

image

整個過程就是創建了一個Ioc 容器,然後根據類型獲取了xml裏面定義的對象而已。接下來,我們先以使用xml配置的這種場景(註解的會隨後講),來講述下xml是怎麼被加載,解析。畫外音:xml這種配置方式現在確實已經不怎麼使用,但是我們先順着這一條路下去講通,然後其他的場景也會容易理解很多。

1. Resource

我們很容易可以想到,使用xml配置的情況下,Spring需要先加載解析我們的xml,在Spring的世界裏,把類似於xml這樣的東西定義爲Resource,對應的接口爲Resource.java,代表了Spring對資源的統一抽象,我們來看看它的代碼:

image

畫外音:後邊還會貼很多這種最頂層的接口,目的是爲了讓大家理解,這個接口的實現類的最核心的行爲,比如說對於Resource,最核心的行爲就是getFile。我們通過方法名字可以可以看出這些方法是想讓它的實現類來做什麼的,在這裏我就不一一解釋。然後我們一起來看看Resource類及其實現類的結構圖(來自網絡):

我們可以看出來,根據資源類型的不同,Spring爲我們提供了不同的實現。其中,AbstractResource是Resource的默認實現,它實現了Resource的大部分接口,這個類也是Resource體系中的重中之重,大部分的子類也是繼承的它的方法。如果我們想要定義一種資源,那麼一定是繼承AbstractResource類,然後根據我們的資源的特點,去覆蓋它的方法。

2. ResourceLoader

Resource這裏說完了,然後我們來說Resource的加載。在Spring裏,Resource和它的加載是分開的,我想這麼設計的道理大家肯定都懂,一方面是單一職責的原則嘛,各個部分只需要專注於自己的事情就好,另外一方面其實就是面向對象思想,比如說吃飯這個過程按照面向對象的思想來設計的話,那就是飯和吃兩個接口,飯就像Resource,而吃就像加載Resource的接口。在Spring中,這個加載Resource的接口是ResourceLoader,還是按照慣例,貼一下它的源碼:

image

這個接口的代碼非常簡單,只有兩個方法,其中最重要的當然是getResource這個方法,其實現類往往是通過這個方法定位到具體的資源實例。它的默認實現是DefaultResourceLoader,當然最核心的方法依然是getResource這個方法,我們一起來看看:

image

我們通過上面的代碼可以看出來一個問題,就是如果localtion是諸如D:/abc/cde/fgh這種格式的時候,它會被解析成一個ClassPathContextResource(因爲顯然location不屬於Url,在 URL url = new URL(location)的時候會拋MalFormedURLException),這個顯然不是我們想要的,我們更希望,這個可以被解析成一個FileSystemResource。這時候,我們可以使用FileSystemResourceLoader中的getResource方法來獲取,具體用法就不在這裏提了。

3. ResourcePatternReslover

上面的ResourceLoader中的getResource方法每次只能加載一個Resource,如果要同時加載某個文件夾下面的所有Resource的話,需要使用到ResourcePatternReslover相關的的實現類,我們還是先來看看ResourcePatternReslover的源碼:

image

我們可以看到這裏只有一個getResources方法,可以根據傳入的路徑來匹配,返回多個資源。注意看哈,其實這個接口還繼承了ResourceLoader這個接口,所以其實它的子類,也是具備ResourceLoader的功能的。同時,ResourcePatternReslover還增加了一種新的協議前綴classpath*,對這一點的支持也是由相應的子類來實現的。它最常用的一個子類是PathMatchingResourceReslover,它能夠像ResourceLoader一樣加載資源(但是注意,這裏它具備ResourceLoader的功能,完全是通過一個委派來實現的)。這個類還支持基於Ant風格的路徑匹配模式(**/*.xml之類的,也就是匹配某個路徑下的資源,然後加載)。關於PathMatchingResourceReslovergetResources方法,由於篇幅原因,就不具體講了,大家可以直接去我的github裏面的代碼看源碼,還能鍛鍊自己閱讀複雜代碼的能力,這一點是很重要的。大家不用害怕,這個方法即使不看不會影響大家閱讀後邊的文章~????

4.ApplicationContext和資源加載的關係

在看到這塊之前,希望大家是可以搞明白上面到底講了什麼的(你最少要明白ResourceResourceLoaderPathMatchingResourceReslover是幹嘛的),如果還不明白,建議先滾動到之前的地方看一看,如果沒看明白的話,接下來的東西看了也不會理解的。我們這篇文章的題目是統一資源的加載,那麼是誰的統一資源的加載呢?答案其實就是ApplicationContext,之所以沒有在開始就說這玩意,是因爲這ApplicationContext的東西太多了,不想讓大家過多關注了這個,現在我們可以來看看部分源碼了:

image

What??說好的源碼你就讓我看個類名?別急????,我們看這個接口所繼承的接口的最後一個,原來這個ApplicationContext繼承了ResourcePatternReslover接口了啊,而我們前面的文章裏面提過ResourcePatternReslover它又繼承了ResourceLoader接口。這裏需要多提一個關於ApplicationContext的點是一般來說,各種各樣場景的AplicationContext比如最開始代碼的ClassPathXmlApplicationContext都是直接或者間接的繼承AbstractApplicationContext,而這個AbstractApplicationContext它又繼承了DefaultResourceLoader,所以說ApplicationContext子類的getResource方法,其實就是DefaultResourceLoader方法,然後需要加載多個資源時,AbstractApplicationContext裏面還有一個ResourcePatternReslover,而它的實例就是PathMatchingResourcePatternReslover。也就是說,在需要進行資源加載的時候,ApplicationContext的實現類們完全就可以作爲一個ResourcePatternReslover或者是ResourceLoader來進行資源的加載,只不過在幹活的時候,還是交給具體的ResourceLoader以及ResourcePatternReslover的實例來的。我們可以看一看UML圖(來自揭祕Spring):

image

推薦閱讀

原創推薦:日誌規範多重要,這篇文章告訴你!

原創推薦:我們已經不用AOP做操作日誌了!

ElasticSearch 使用 Logstash 從 MySQL 中同步數據

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章