最近看了一些 Spring 原始碼,發現原始碼分析的文章很多,而底層思想分析的文章比較少,這個系列文章準備總結一下Spring中給我的啟示,包括設計模式思想、SOLID設計原則等,涉及一些編程的基本原則,雖然看似簡單,實則“小道理、大學問”,
我盡量遇到的問題談起,再說解決方案,同時至少舉兩個例子,
這些方法都是基于我遇到的一些實際代碼,掌握了基本思想,就可以舉一反三,
讓人頭暈眼花的跳轉
如果你通過某些培訓機構的原始碼課,就會發現他們的老師在講原始碼的時候在類之間、方法之間不停地跳,學員一臉懵逼,因為如果不理解老師講課的思路,或者是稍微走一下神,就會覺得自己跟不上了,
其實,問題就在于需要理解原始碼的基本流程和繼承鏈的這種單一職責原則,
讀懂繼承鏈
面向物件的特點是封裝繼承和多型,封裝體現在私有方法,
在Spring中,常見的模式是頂層為介面,然后是抽象類,最后是各個實作類,
介面負責對外暴露,符合面向介面編程的準則,在更改實作類時,可以減小對于代碼的改動,保證了代碼依賴于抽象(介面),.
抽象類一般使用模板模式,實作了邏輯的組裝,把子類中的公用邏輯抽取出來,方便子類撰寫;模板方法一般為固定的執行鏈,我們讀原始碼時,可以予以關注,比如常見的AbstractApplicationContext::refresh 方法,AbstractBeanFactory::doGetBean方法,由于介面一般只能暴露方法宣告,抽象類可以實作一些狀態的getter,settter,這樣子類訪問這些狀態資料只需要呼叫方法即可,
實作類通常有多個,比如todo,有些時候當實作類只有一個或者有一個默認實作類時,常常使用default命名,比如DefaultListableBeanFactory. 讀原始碼時可以選擇地閱讀默認實作,
Java 只支持單繼承,這種語言層面的規范方便了我們閱讀原始碼,一個方法的實作必然在一條繼承鏈中,
舉例1:

Spring中BeanFactory的默認實作是DefaultListableBeanFactory, 通過繼承圖可以看出,有多個抽象類,從上到下分別實作了以下的能力:
- 別名注冊(
SimpleAliasRegistry) - 單例注冊(
DefaultSingletonBeanRegistry) - FactoryBean 注冊(
FactoryBeanRegistrySupport) - bean工廠介面(主要就是getBean相關的一些方法)(
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:

類似如上的分析,AnnotationConfigApplicationContext 的繼承鏈如下:
DefaultResourceLoader → AbstractApplicationContext → GenericApplicationContext → AnnotationConfigApplicationContext
每個類實作的功能即其直接實作的介面,有些類通過類名也可以快速得知其實作的功能,不再贅述,
GenericApplicationContext 實作了BeanDefinitionRegistry,直接委托BeanFactory的實作給DefaultListableBeanFactory,子類包括AnnotationConfigApplicationContext 和mvc容器等,
在抽象類AbstractApplicationContext可以看到大家耳熟能詳的refresh方法,
ApplicationContext更是重量級,作為應用容器,擁有BeanFactory外的事件廣播、國際化、資源讀取等能力,
由于ApplicatoinContext是大介面,是不同功能的最終整合,所以我們看到的介面繼承關系并不復雜,
舉例3:

我們知道MVC模型中具有中央調度器,在SpringMvc中體現為DispatcherServlet,其繼承鏈如圖,
了解過servlet的人都知道HttpServlet具有doGet、doPost等方法,子類重新后所有方法都轉發到DispatcherServlet中,doService→doDispatch執行了我們熟知的分發模板邏輯:簡單來說就是
getHandler → getHandlerAdapter → applyPreHandle → **handle** → applyPostHandle → processDispatchResult
processDispatchResult中包含例外處理,render和afterCompletion
我們隨便選擇一個方法,比如初始化mvc容器,其必在繼承鏈上的某個類中進行實作,通過分析原始碼可以看出:
initWebApplicationContext在FrameworkServlet中實作,GenericServlet暴露init方法,
HttpServletBean實作了init方法,在init方法中暴露
initServletBean方法,FrameworkServlet實作
initServletBean方法,其中實作了initWebApplicationContext方法,
雖然執行初始化mvc容器方法需要在繼承鏈上來回跳轉,但是其實作了單一職責原則,每一個類負責實作了特定的功能,模板類實作了模板流程,實作類實作具體實作,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/507133.html
標籤:Java
上一篇:Redis變慢?深入淺出Redis性能診斷系列文章(三)
下一篇:java中如何生成UUID呢?
