框架源碼閱讀的方法與技巧

代碼是形式,邏輯是神韻。html


引子

「解鎖優秀源代碼的基本方法與技巧」 一文中,探討了閱讀優秀源碼的基本步驟、方法、技巧、所面臨的障礙及克服之策。多加訓練,應該能夠達成以下目標:程序員

  • 可以讀懂獨立類和基本容器的實現;
  • 可以讀懂小型的基礎庫和框架;
  • 經過源碼閱讀來調試和解決實際中的問題。

本文進一步探索如何閱讀成熟框架的源碼。

web

舒適提示

欲速則不達。閱讀源碼很容易理解爲就是直接去閱讀代碼自己。實際上,代碼只是形式,邏輯纔是神韻。面試

凡有助於去理解邏輯,理解其原理、架構、實現的,都是值得閱讀的。包括而不限於官方文檔和 API 文檔、架構設計分析文章、原理分析文章、源碼閱讀分析文章。磨刀不誤砍柴工。準備工做作充足,充分藉助各類資源輔助,閱讀源碼才能事半功倍。算法


預思考

有需求才有目標,有目標纔有設計,有設計纔有框架。在閱讀某個源碼模塊以前,思考若干基本問題是必要的。編程

  • 需求是什麼?用一句話說清楚;
  • 設計目標是什麼?用一句話說清楚;
  • 核心優點和適用場景是什麼?分別用一句話說清楚;
  • 基本原理是怎樣的?先本身思考怎麼實現,而後閱讀框架原理文章;
  • 總體設計是怎樣的? 先本身思考怎麼設計,而後閱讀架構設計的文章;
  • 技術難點是什麼?先本身思考其中的難點及解決方案,而後閱讀相關文章;
  • 數據結構及算法流程是如何設計的?閱讀框架的源碼解析文章。

好比 SpringBean 模塊:設計模式

  • 需求:有一套通用機制去建立和裝配應用所須要的完整的 Bean 實例,使得應用無需關注 Bean 實例的建立和管理,只要按需獲取;
  • 設計目標:根據指定的配置文件或註解,生成和存儲應用所須要的裝配完整的 Bean 實例,並提供多種方式來獲取 Bean 實例;
  • 核心優點:支持多種裝配方式、自動裝配、依賴關係自動注入;支持不一樣做用域的 Bean 實例建立和獲取;穩定高效;
  • 適用場景:有大量的 Bean 須要建立,這些 Bean 存在複雜的依賴關係;
  • 基本原理:反射機制 + 緩存;
  • 算法流程:建立 bean 工廠對象 -> 掃描資源路徑,得到 bean 的 class 文件 -> 生成 bean 定義的 beanDefinition 實例 -> 根據 beanDefinitioin 實例建立 bean 實例並緩存到 bean 工廠對象 -> 依賴自動注入 -> 執行鉤子方法 -> 完整的 bean 實例準備就緒。
  • 技術難點:依賴自動裝配、循環引用;解決自動依賴注入和循環引用問題須要用到緩存機制。

需求與目標緩存

需求與目標每每容易混爲一談。但需求不等於目標。網絡

  • 需求是寬泛的,目標是具體的;
  • 目標是需求的一種實現途徑,每每是設計一個具有某些關鍵特性的系統或產品。

目標是功能與質量的結合體;除了功能部分,肯定質量指標也是尤其關鍵的。質量指標可參閱:「Web服務端軟件的服務品質概要」數據結構

對於某個框架來講,需求、適用場景和核心優點,都是能夠直接在官網或項目主頁獲取到的。如何還原框架的設計目標呢?能夠從核心優點中獲取基本說明,更多的就要從 API 文檔裏來提煉了。

方法

不少開發童鞋可能對閱讀源碼心生畏懼。其實讀源碼既不神祕也不復雜:寫個 Demo,打斷點,運行,而後細細揣摩。閱讀源碼就是觀摩高手出招的過程。

  1. 確立目標,一般是理解某個模塊的原理、設計或者爲了解決實際問題;
  2. 寫個 demo,可以將主流程運行起來;
  3. 找到框架運行的入口點,經過靜態代碼分析,大體瞭解整個實現流程;
  4. 在預估會通過的關鍵地方打斷點,單步調試;
  5. 仔細查看主流程通過的主路徑、每個主要對象及其成員變量的值及變化,細細揣摩其設計意圖和方法技巧;
  6. 繪製總體流程框圖和類的交互圖;
  7. 學習和理解關鍵類及關鍵方法及實現(代碼)。

閱讀源碼,要把握主要與擴展:

  • 首先把主流程及涉及到的主要類弄透徹;
  • 理解其擴展機制;
  • 理解主要擴展實現(須要的時候徐圖之)。

閱讀源碼,經常要將「靜態代碼分析」和「單步調試」結合起來使用。

靜態代碼分析

靜態代碼分析,就是沿着方法調用鏈,「順藤摸瓜」一路點擊下去。一般可以對總體流程有一個大概的瞭解。

因爲框架實現經常基於接口編程,有時會遇到有多個實現的情形。這時,能夠根據直覺和經驗,選擇一個最有可能的默認實現繼續跟下去,或者經過單步調試來弄清楚是哪一個具體實現。

單步調試

單步調試,是看似笨拙卻很實用的源碼閱讀方法。單步調試在如下情形尤爲有用:

  • 接口調用有多個實現,難以肯定是哪一個是具體實現時;
  • 查看某個比較複雜的具體類的成員時;
  • 理解實現細節時。

框架解析

框架的設計實現一般包括三層:

  • 問題域及解決方案構成的抽象層,解決問題的核心部分;
  • 封裝和交互構成的設計層,確保靈活性、可擴展性和應用集成;
  • 各類細節構成的實現層,用於保證性能和容錯等。

閱讀順序是:抽象層 -> 封裝與交互層 -> 細節實現層 或者 抽象層 -> 細節實現層 -> 封裝與交互層。抽象層比如匣中的寶珠,不能幹買櫝還珠的事情。

抽象層

抽象層便是問題求解層。技術面試中問到的原理或實現機制,一般都屬於這一層。

因爲封裝和交互、實現細節的大量代碼每每會將用於解決問題的核心代碼「淹沒」,所以,在探索抽象層時,要學會大膽過濾封裝和細節,直接跳過大量的分支條件語句,暫時跳過使人疑惑的地方,始終聚焦和直擊解決問題的核心部分。用於解決基本問題的核心代碼一般是很少的。

好比,Bean 實例建立的核心代碼是 ClassPathBeanDefinitionScanner.doScan(掃描資源路徑,生成 beanDefinition 對象) 和AbstractAutowireCapableBeanFactory.doCreateBean 方法(根據 beanDefinition 建立 bean 實例)。

設計層

要弄明白設計層,就要先弄清楚框架的總體設計:

  • 有哪些子模塊,子模塊的設計意圖是什麼;
  • 子模塊之間的關聯是怎樣的,如何串聯成一個完整的設計意圖。

框架的設計實現經常會用到設計模式。

  • 經常使用設計模式:工廠、單例、外觀、策略、適配器、裝飾、代理、模板、組合、觀察者、迭代器;
  • 不一樣問題域可能會用到的設計模式,好比 DB 驅動接口實現會用到生成器模式和橋接模式,web 請求處理用到職責鏈模式。

經常使用設計模式的使用場景:

  • 若是須要建立實例,則一般離不開工廠和單例模式;
  • 若是涉及較爲複雜的算法流程,部分算法須要在子類實現,則會用到模板方法模式;
  • 若是須要多種實現,並依據特定場景來選取使用,則會用到策略模式;
  • 若是要將客戶端接口及實現與框架的調用隔離,則會用到動態代理模式;
  • 若是要靈活疊加多種功能,則會用到裝飾器模式;
  • 若是涉及到事件機制,則離不開觀察者模式;
  • 若是須要在庫實現的基礎上提供簡潔接口,則一般用到外觀模式;
  • 若是要將多種實現與多種接口定義進行鏈接,則會用到橋接模式;
  • 若是須要涉及大量配置(規格)並生成實例,則一般用到生成器模式;
  • 若是涉及容器元素訪問,則離不開迭代器模式;
  • 若是須要以統一接口訪問總體與部分的行爲,且總體由部分組成,則一般用到組合模式。

理解基本設計模式的特徵和適用場景,識別設計模式的使用,能夠更自如地在框架源碼之間穿梭。

細節層

細節是最考驗源碼閱讀的心性了。細節藏魔鬼。關鍵細節考慮不周全,可能會致使整個設計的失敗。所以,細節層也是值得仔細推敲的。技術面試中也經常考察實現細節。若是可以回答上來,大機率會讓面試官眼前一亮。

有時,一些實現細節可能讓人摸不到頭腦。此時,能夠上網搜索一下,每每會「茅塞頓開」。

克服障礙

閱讀成熟框架源碼,遇到的一大挑戰就是對象之間的錯綜複雜的交互關係。使人生畏。這實際上考驗着開發者的抽象和建模能力。

原理流程圖

原理流程圖很是重要,就像地圖同樣,指引人更容易地在「代碼迷宮」中穿行而不迷失方向。

在閱讀源碼以前,設法弄到並理解框架的原理流程圖,每每能起到事半功倍的效果。就如行兵打仗,先弄清楚天時與地形。不可不重視之。

概念圖景

優秀的軟件設計,每每是先創建一個比較完整的概念圖景。概念圖景,就是關於某個問題域的概念及其關聯關係的總體圖。

譬如蓋房子吧。有的人蓋房子就是:砌磚!砌磚!!砌磚!!!要安裝窗戶怎麼辦?把其中一大塊磚牆錘空了再安。

有的人,則會「設計先行」:

  • 原材料 => 子部件 => 組合與集成。
  • 原材料:磚、石、木、鋁、銅、玻璃等;
  • 子部件:牆、窗框、窗戶、門、地板、樓梯、鎖、通道等;
  • 房子:由子部件進行組合和集成而成;
  • 機制:子部件的組合與集成的原理支撐,好比形狀的組合與契合、承壓計算等。

如何理清其中的複雜交互關係,從而理解其中蘊含的設計思想呢?須要先理清楚框架的概念圖景。

有兩種技巧能夠結合使用:

  • 因爲接口定義了具體類的行爲規範,能夠經過閱讀接口定義及文檔來了解其設計思路和骨架;
  • 查看具體類的實例成員(暫不涉及方法),根據經驗揣摩其設計意圖。

核心類成員

要深刻到具體實現,則沒法避免核心類的閱讀。核心類每每擁有十幾個甚至幾十個成員及方法,展現出了十足的源碼閱讀勸退誠意。面對這種狀況怎麼辦呢? 有三個技巧能夠結合使用:

  • 按快捷鍵 Alt+7,能夠查看該類的全部成員及方法,概覽一下,大體猜想其意圖;
  • 首先只關注那些對核心問題求解有重要影響的成員,暫時忽略那些用來提高性能、可擴展性等方面的成員;
  • 單步調試,仔細看看運行時的成員對象如何,這樣會更加直觀具體一些。好比 DefaultListableBeanFactory 這個類,單步調試後獲得以下圖示:

技術難點

技術難點也是理解源碼實現的一個主要障礙。技術難點主要有三類:

  • 數據結構與算法:好比 HashMap 用到了哈希表和紅黑樹,須要先閱讀文獻(好比《算法導論》)理解其結構與算法;
  • 原理機制:好比 IO 讀寫、內存管理、文件系統、編譯原理、網絡協議,先學習相關的原理機制,夯實基礎;
  • 編程模型:特別的編程手法和技巧,好比讀 hystrix 源碼,就要先熟悉函數式編程和響應式編程。

如何找到論述原理機制的相關文獻呢?有一些基本方法可循:

  • 經典書籍:好比數據結構與算法,就有《算法導論》、《算法》、《計算機程序設計藝術》(排序與查找)等;
  • 經典論文:一些還沒來得及寫入書籍的論文解讀,好比 Raft 算法等;
  • 技術書籍:好比 Linux 操做系統內核實現,TCP 協議詳解等;
  • 官方文檔:優秀項目主頁的文檔部分,每每有相關原理機制的介紹,好比 ES 的官方文檔;
  • JavaDoc:優秀源碼的 Java Doc 每每會引用相關出處,好比 AQS 的源碼;
  • 優秀博文:優秀博文每每有一些文獻引用,能夠閱讀相關文獻引用;
  • 百科與搜索:在維基百科上搜索出處和引用來源;或者使用搜索引擎。

越到後面,就會發現,真正須要仔細閱讀和鑽研的書籍和論文,其實並很少。花費不少時間閱讀網絡文章,這些偷懶和捷徑,反而是走了彎路。


耐心與意志

閱讀框架源碼須要很大的耐心和意志。有點像蠶寶寶吃桑葉,須要一點一點地啃。各個擊破。在這個過程當中,須要克服很多障礙,才能「修得正果」。

可使用多種輔助手段:

  • 邊聽音樂邊閱讀代碼;
  • 拉取代碼分支,邊讀邊作標記並提交;
  • 閱讀原理、架構及源碼分析文章。

小結

源碼閱讀技能,能夠說是程序員的「內功心法」之一。如果能讀通優秀源碼,則應對平常編程工做遊刃有餘,而應對難題則有路可循。

路漫漫其修遠兮,吾將上下而求索。

相關文章
相關標籤/搜索