優雅的閱讀框架源碼
代碼是形式,邏輯是神韻。
引子
通過過來人的經驗,探討如何優雅的閱讀成熟框架的源碼。
溫馨提示
欲速則不達。閱讀源碼很容易理解爲就是直接去閱讀代碼本身。實際上,代碼只是形式,邏輯纔是神韻。
凡是有助於去理解邏輯,理解其原理、架構、實現的,都是值得閱讀的。包括而不限於官方文檔和 API 文檔、架構設計分析文章、原理分析文章、源碼閱讀分析文章。磨刀不誤砍柴工。準備工作做充足,充分藉助各種資源輔助,閱讀源碼才能事半功倍。
預思考
有需求才有目標,有目標纔有設計,有設計纔有框架。在閱讀某個源碼模塊之前,思考若干基本問題是必要的。
-
需求是什麼?用一句話說清楚;
-
設計目標是什麼?用一句話說清楚;
-
核心優勢和適用場景是什麼?分別用一句話說清楚;
-
基本原理是怎樣的?先自己思考怎麼實現,然後閱讀框架原理文章;
-
整體設計是怎樣的?先自己思考怎麼設計,然後閱讀架構設計的文章;
-
技術難點是什麼?先自己思考其中的難點及解決方案,然後閱讀相關文章;
-
數據結構及算法流程是如何設計的?閱讀框架的源碼解析文章。
比如 SpringBean 模塊:
-
需求:有一套通用機制去創建和裝配應用所需要的完整的 Bean 實例,使得應用無需關注 Bean 實例的創建和管理,只要按需獲取;
-
設計目標:根據指定的配置文件或註解,生成和存儲應用所需要的裝配完整的 Bean 實例,並提供多種方式來獲取 Bean 實例;
-
核心優勢:支持多種裝配方式、自動裝配、依賴關係自動注入;支持不同作用域的 Bean 實例創建和獲取;穩定高效;
-
適用場景:有大量的 Bean 需要創建,這些 Bean 存在複雜的依賴關係;
-
基本原理:反射機制 + 緩存;
-
算法流程:
創建 bean 工廠對象
->掃描資源路徑,獲得 bean 的 class 文件
->生成 bean 定義的 beanDefinition 實例
->根據 beanDefinitioin 實例創建 bean 實例並緩存到 bean 工廠對象
->依賴自動注入
->執行鉤子方法
->完整的 bean 實例準備就緒
。 -
技術難點:依賴自動裝配、循環引用;解決自動依賴注入和循環引用問題需要用到緩存機制。
需求與目標
需求與目標往往容易混爲一談。但需求不等於目標。
-
需求是寬泛的,目標是具體的;
-
目標是需求的一種實現途徑,往往是設計一個具備某些關鍵特性的系統或產品。
目標是功能與質量的結合體;除了功能部分,確定質量指標也是尤爲關鍵的。
對於某個框架來說,需求、適用場景和核心優勢,都是可以直接在官網或項目主頁獲取到的。如何還原框架的設計目標呢?可以從核心優勢中獲取基本說明,更多的就要從 API 文檔裏來提煉了。
方法
很多開發童鞋可能對閱讀源碼心生畏懼。其實讀源碼既不神祕也不復雜:寫個 Demo,打斷點,運行,然後細細揣摩。閱讀源碼就是觀摩高手出招的過程。
-
確立目標,通常是理解某個模塊的原理、設計或者爲了解決實際問題;
-
寫個 demo,能夠將主流程運行起來;
-
找到框架運行的入口點,通過靜態代碼分析,大致瞭解整個實現流程;
-
在預估會經過的關鍵地方打斷點,單步調試;
-
仔細查看主流程經過的主路徑、每一個主要對象及其成員變量的值及變化,細細揣摩其設計意圖和方法技巧;
-
繪製整體流程框圖和類的交互圖;
-
學習和理解關鍵類及關鍵方法及實現(代碼)。
閱讀源碼,要把握主要與擴展:
-
首先把主流程及涉及到的主要類弄透徹;
-
理解其擴展機制;
-
理解主要擴展實現(需要的時候徐圖之)。
閱讀源碼,常常要將 “靜態代碼分析” 和“單步調試”結合起來使用。
靜態代碼分析
靜態代碼分析,就是沿着方法調用鏈,“順藤摸瓜” 一路點擊下去。通常能夠對整體流程有一個大概的瞭解。
由於框架實現常常基於接口編程,有時會遇到有多個實現的情形。這時,可以根據直覺和經驗,選擇一個最有可能的默認實現繼續跟下去,或者通過單步調試來弄清楚是哪個具體實現。
單步調試
單步調試,是看似笨拙卻很實用的源碼閱讀方法。單步調試在以下情形尤其有用:
-
接口調用有多個實現,難以確定是哪個是具體實現時;
-
查看某個比較複雜的具體類的成員時;
-
理解實現細節時。
框架解析
框架的設計實現通常包括三層:
-
問題域及解決方案構成的抽象層,解決問題的核心部分;
-
封裝和交互構成的設計層,確保靈活性、可擴展性和應用集成;
-
各種細節構成的實現層,用於保證性能和容錯等。
閱讀順序是:抽象層 -> 封裝與交互層 -> 細節實現層 或者 抽象層 -> 細節實現層 -> 封裝與交互層。抽象層好比匣中的寶珠,不能幹買櫝還珠的事情。
抽象層
抽象層即是問題求解層。技術面試中問到的原理或實現機制,通常都屬於這一層。
由於封裝和交互、實現細節的大量代碼往往會將用於解決問題的核心代碼 “淹沒”,因此,在探索抽象層時,要學會大膽過濾封裝和細節,直接跳過大量的分支條件語句,暫時跳過令人疑惑的地方,始終聚焦和直擊解決問題的核心部分。用於解決基本問題的核心代碼通常是不多的。
比如,Bean 實例創建的核心代碼是 ClassPathBeanDefinitionScanner.doScan(掃描資源路徑,生成 beanDefinition 對象) 和 AbstractAutowireCapableBeanFactory.doCreateBean 方法(根據 beanDefinition 創建 bean 實例)。
設計層
要弄明白設計層,就要先弄清楚框架的整體設計:
-
有哪些子模塊,子模塊的設計意圖是什麼;
-
子模塊之間的關聯是怎樣的,如何串聯成一個完整的設計意圖。
框架的設計實現常常會用到設計模式。
-
常用設計模式:工廠、單例、外觀、策略、適配器、裝飾、代理、模板、組合、觀察者、迭代器;
-
不同問題域可能會用到的設計模式,比如 DB 驅動接口實現會用到生成器模式和橋接模式,web 請求處理用到職責鏈模式。
常用設計模式的使用場景:
-
如果需要創建實例,則通常離不開工廠和單例模式;
-
如果涉及較爲複雜的算法流程,部分算法需要在子類實現,則會用到模板方法模式;
-
如果需要多種實現,並依據特定場景來選取使用,則會用到策略模式;
-
如果要將客戶端接口及實現與框架的調用隔離,則會用到動態代理模式;
-
如果要靈活疊加多種功能,則會用到裝飾器模式;
-
如果涉及到事件機制,則離不開觀察者模式;
-
如果需要在庫實現的基礎上提供簡潔接口,則通常用到外觀模式;
-
如果要將多種實現與多種接口定義進行連接,則會用到橋接模式;
-
如果需要涉及大量配置(規格)並生成實例,則通常用到生成器模式;
-
如果涉及容器元素訪問,則離不開迭代器模式;
-
如果需要以統一接口訪問整體與部分的行爲,且整體由部分組成,則通常用到組合模式。
理解基本設計模式的特徵和適用場景,識別設計模式的使用,可以更自如地在框架源碼之間穿梭。
細節層
細節是最考驗源碼閱讀的心性了。細節藏魔鬼。關鍵細節考慮不周全,可能會導致整個設計的失敗。因此,細節層也是值得仔細推敲的。技術面試中也常常考察實現細節。如果能夠回答上來,大概率會讓面試官眼前一亮。
有時,一些實現細節可能讓人摸不到頭腦。此時,可以上網搜索一下,往往會 “茅塞頓開”。
克服障礙
閱讀成熟框架源碼,遇到的一大挑戰就是對象之間的錯綜複雜的交互關係。令人生畏。這實際上考驗着開發者的抽象和建模能力。
原理流程圖
原理流程圖非常重要,就像地圖一樣,指引人更容易地在 “代碼迷宮” 中穿行而不迷失方向。
在閱讀源碼之前,設法弄到並理解框架的原理流程圖,往往能起到事半功倍的效果。就如行兵打仗,先弄清楚天時與地形。不可不重視之。
概念圖景
優秀的軟件設計,往往是先建立一個比較完整的概念圖景。概念圖景,就是關於某個問題域的概念及其關聯關係的整體圖。
譬如蓋房子吧。有的人蓋房子就是:砌磚!砌磚!!砌磚!!!要安裝窗戶怎麼辦?把其中一大塊磚牆錘空了再安。
有的人,則會 “設計先行”:
-
原材料 => 子部件 => 組合與集成。
-
原材料:磚、石、木、鋁、銅、玻璃等;
-
子部件:牆、窗框、窗戶、門、地板、樓梯、鎖、通道等;
-
房子:由子部件進行組合和集成而成;
-
機制:子部件的組合與集成的原理支撐,比如形狀的組合與契合、承壓計算等。
如何理清其中的複雜交互關係,從而理解其中蘊含的設計思想呢?需要先理清楚框架的概念圖景。
有兩種技巧可以結合使用:
-
由於接口定義了具體類的行爲規範,可以通過閱讀接口定義及文檔來了解其設計思路和骨架;
-
查看具體類的實例成員(暫不涉及方法),根據經驗揣摩其設計意圖。
核心類成員
要深入到具體實現,則無法避免核心類的閱讀。核心類往往擁有十幾個甚至幾十個成員及方法,展示出了十足的源碼閱讀勸退誠意。面對這種情況怎麼辦呢?有三個技巧可以結合使用:
-
按快捷鍵 Alt+7,可以查看該類的所有成員及方法,概覽一下,大致猜測其意圖;
-
首先只關注那些對核心問題求解有重要影響的成員,暫時忽略那些用來提升性能、可擴展性等方面的成員;
-
單步調試,仔細看看運行時的成員對象如何,這樣會更加直觀具體一些。比如 DefaultListableBeanFactory 這個類,單步調試後得到如下圖示:
技術難點
技術難點也是理解源碼實現的一個主要障礙。技術難點主要有三類:
-
數據結構與算法:比如 HashMap 用到了哈希表和紅黑樹,需要先閱讀文獻(比如《算法導論》)理解其結構與算法;
-
原理機制:比如 IO 讀寫、內存管理、文件系統、編譯原理、網絡協議,先學習相關的原理機制,夯實基礎;
-
編程模型:特別的編程手法和技巧,比如讀 hystrix 源碼,就要先熟悉函數式編程和響應式編程。
如何找到論述原理機制的相關文獻呢?有一些基本方法可循:
-
經典書籍:比如數據結構與算法,就有《算法導論》、《算法》、《計算機程序設計藝術》(排序與查找)等;
-
經典論文:一些還沒來得及寫入書籍的論文解讀,比如 Raft 算法等;
-
技術書籍:比如 Linux 操作系統內核實現,TCP 協議詳解等;
-
官方文檔:優秀項目主頁的文檔部分,往往有相關原理機制的介紹,比如 ES 的官方文檔;
-
JavaDoc:優秀源碼的 Java Doc 往往會引用相關出處,比如 AQS 的源碼;
-
優秀博文:優秀博文往往有一些文獻引用,可以閱讀相關文獻引用;
-
百科與搜索:在維基百科上搜索出處和引用來源;或者使用搜索引擎。
越到後面,就會發現,真正需要仔細閱讀和鑽研的書籍和論文,其實並不多。花費很多時間閱讀網絡文章,這些偷懶和捷徑,反而是走了彎路。
耐心與意志
閱讀框架源碼需要很大的耐心和意志。有點像蠶寶寶喫桑葉,需要一點一點地啃。各個擊破。在這個過程中,需要克服不少障礙,才能 “修得正果”。
可以使用多種輔助手段:
-
邊聽音樂邊閱讀代碼;
-
拉取代碼分支,邊讀邊做標記並提交;
-
閱讀原理、架構及源碼分析文章。
小結
源碼閱讀技能,可以說是程序員的 “內功心法” 之一。若是能讀通優秀源碼,則應對日常編程工作遊刃有餘,而應對難題則有路可循。
路漫漫其修遠兮,吾將上下而求索。
(感謝閱讀,希望對你所有幫助)
來源:cnblogs.com/lovesqcc/p/14403497.html
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/FOBvCCEa-cuDxEpbXo74xw