天天看點

從Spring中學到的--讀懂繼承鍊

最近看了一些 Spring 源碼,發現源碼分析的文章很多,而底層思想分析的文章比較少,這個系列文章準備總結一下Spring中給我的啟示,包括設計模式思想、SOLID設計原則等,涉及一些程式設計的基本原則,雖然看似簡單,實則“小道理、大學問”。

我盡量遇到的問題談起,再說解決方案,同時至少舉兩個例子。

這些方法都是基于我遇到的一些實際代碼,掌握了基本思想,就可以舉一反三。

讓人頭暈眼花的跳轉

如果你通過某些教育訓練機構的源碼課,就會發現他們的老師在講源碼的時候在類之間、方法之間不停地跳,學員一臉懵逼。因為如果不了解老師講課的思路,或者是稍微走一下神,就會覺得自己跟不上了。

其實,問題就在于需要了解源碼的基本流程和繼承鍊的這種單一職責原則。

讀懂繼承鍊

面向對象的特點是封裝繼承和多态,封裝展現在私有方法。

在Spring中,常見的模式是頂層為接口,然後是抽象類,最後是各個實作類。

接口負責對外暴露,符合面向接口程式設計的準則,在更改實作類時,可以減小對于代碼的改動,保證了代碼依賴于抽象(接口)。.

抽象類一般使用模闆模式,實作了邏輯的組裝,把子類中的公用邏輯抽取出來,友善子類編寫;模闆方法一般為固定的執行鍊,我們讀源碼時,可以予以關注。比如常見的AbstractApplicationContext::refresh 方法,AbstractBeanFactory::doGetBean方法。由于接口一般隻能暴露方法聲明,抽象類可以實作一些狀态的getter,settter,這樣子類通路這些狀态資料隻需要調用方法即可。

實作類通常有多個,比如todo。有些時候當實作類隻有一個或者有一個預設實作類時,常常使用default命名,比如 ​

​DefaultListableBeanFactory.​

​ 讀源碼時可以選擇地閱讀預設實作。

Java 隻支援單繼承,這種語言層面的規範友善了我們閱讀源碼,一個方法的實作必然在一條繼承鍊中。

舉例1:

從Spring中學到的--讀懂繼承鍊

Spring中BeanFactory的預設實作是 ​

​DefaultListableBeanFactory​

​ , 通過繼承圖可以看出,有多個抽象類,從上到下分别實作了以下的能力:

SimpleAliasRegistry
DefaultSingletonBeanRegistry
FactoryBeanRegistrySupport
AbstractBeanFactory
AbstractAutowireCapableBeanFactory      

同時我們還可以看到,基本上每一個抽象類都對應一個實作的接口:

​AliasRegistry​

​ ← ​

​SimpleAliasRegistry​

​SingletonBeanRegistry​

​ ← ​

​DefaultSingletonBeanRegistry​

​AbstractBeanFactory​

​ ← ​

​ConfigurableBeanFactory​

​AbstractAutowireCapableBeanFactory​

​ ← ​

​AutowireCapableBeanFactory​

注意到 DefaultListableBeanFactory 實作了 ​

​ConfigurableListableBeanFactory​

​ ,這個接口實作了一些簡化配置 beanFactory 的方法,是一個常用的基礎設施類(接口)。

實際上,這個設計也是不得已而為之。Java隻支援單繼承,理想的情況下, ​

​DefaultListableBeanFactory​

​ 需要繼承不同的trait,即單例注冊、FactoryBean注冊等功能子產品,Configurable, Listable, **Capable恰好是這種設計思想的展現。如果最終的DefaultListableBeanFactory寫成一個類,一定是巨大的,但是假如我們将BeanFactory不同的特性做拆分的話,就會得到如圖所示的看似複雜的接口繼承關系。

這種松散的接口繼承關系正是我們需要的,舉例來說,autowireCapable 和 hierarchical并沒有實際上的聯系,一個關注屬性注入,另一個則關注bean工廠的層級關系(可以有父工廠)。

假設每一個松散的接口都有幾個或多個實作,不管其是否是抽象類或者具體實作,我們隻有通過多繼承或者委托模式組裝得到 ​

​DefaultListableBeanFactory​

​ 。

這就是沖突的地方,單一的繼承鍊和松散的接口,其結果就是抽象類具有了一些不必要的功能。比如 ​

​AbstractAutowireCapableBeanFactory​

​ 具有了Configurable、aliasRegistry等能力。這種情況是我們閱讀源碼是需要注意的。

Spring通過将單繼承鍊分解為6個類,将 ​

​DefaultListableBeanFactory​

​ 進行了功能拆分,符合開閉原則,每個類也符合單一職責原則的要求。

通過以上分析,打開 ​

​DefaultListableBeanFactory​

​ 的源碼,雖然有2000多行,我們可以清楚地看出類的結構,包括1\. 繼承鍊相關:不同抽象方法的實作、未在抽象類中實作的接口方法的實作。2. ​

​BeanDefinitionRegistry​

​ 3. ​

​ConfigurableListableBeanFactory​

​ 4. ​

​Serializable​

舉例2:

從Spring中學到的--讀懂繼承鍊

類似如上的分析,AnnotationConfigApplicationContext 的繼承鍊如下:

DefaultResourceLoader → AbstractApplicationContext → GenericApplicationContext → AnnotationConfigApplicationContext

每個類實作的功能即其直接實作的接口,有些類通過類名也可以快速得知其實作的功能。不再贅述。

GenericApplicationContext 實作了BeanDefinitionRegistry,直接委托BeanFactory的實作給DefaultListableBeanFactory,子類包括AnnotationConfigApplicationContext 和mvc容器等。

在抽象類AbstractApplicationContext可以看到大家耳熟能詳的refresh方法。

ApplicationContext更是重量級,作為應用容器,擁有BeanFactory外的事件廣播、國際化、資源讀取等能力。

由于ApplicatoinContext是大接口,是不同功能的最終整合,是以我們看到的接口繼承關系并不複雜。

舉例3:

從Spring中學到的--讀懂繼承鍊

我們知道MVC模型中具有中央排程器,在SpringMvc中展現為DispatcherServlet,其繼承鍊如圖。

了解過servlet的人都知道HttpServlet具有doGet、doPost等方法,子類重新後所有方法都轉發到DispatcherServlet中,doService→doDispatch執行了我們熟知的分發模闆邏輯:簡單來說就是

​getHandler​

​ → ​

​getHandlerAdapter​

​ → ​

​applyPreHandle​

​ → ​

​**handle**​

​ → ​

​applyPostHandle​

​ → ​

​processDispatchResult​

​processDispatchResult中包含異常處理,render和afterCompletion​

我們随便選擇一個方法,比如初始化mvc容器,其必在繼承鍊上的某個類中進行實作,通過分析源碼可以看出:

​initWebApplicationContext​

​ 在FrameworkServlet中實作。

GenericServlet暴露init方法。