首頁>Club>
10
回覆列表
  • 1 # 程式設計師小軒

    閱讀原始碼是每個優秀開發工程師的必經之路,那麼這篇文章就來講解下為什麼要閱讀原始碼以及如何閱讀原始碼。

    首先來說下為什麼要讀原始碼,有學習原始碼的必要嗎?

    為什麼要閱讀原始碼?

    關於為什麼閱讀和學習原始碼,我個人認為可能有以下幾點:

    (一)吊打面試官,應對面試

    為了找到更好的工作,應對面試,因為在面試中肯定會問到原始碼級別的問題,比如:為什麼 HashMap 是執行緒不安全的?

    如果你沒有閱讀過原始碼,面試官可能會對回答的結果不滿意,進而導致面試結果不太理想,但如果你對原始碼有所研究,並能夠很好地問答面試官的問題,這可能就是你的加分點,可以形成自己獨特的競爭力,吊打面試官,升職加薪不是夢。

    (二)解決問題(bug)

    在開發過程中,我們或多或少會遇到 bug,比如:在 foreach 迴圈裡進行元素的 remove/add 操作,為啥有可能會報 ConcurrentModificationException 異常?

    我們可以先在 Google、Stack Overflow 以及對應專案的 Issues 裡看有沒有類似問題以及解決辦法,如果沒有的話,我們只能透過閱讀原始碼的方式去解決了。如果我們對相關原始碼有所涉獵,就可以快速定位到問題所在。

    (三)提升程式設計能力

    讀一本好書,就是和許多高尚的人談話。 -歌德

    和閱讀一本好書一樣,閱讀原始碼就是和程式設計大牛面對面交流的機會,在許多優秀的開源專案中,它們的編碼規範和架構設計都是很棒的,另外在設計上也使用了大量的設計模式,透過閱讀和學習原始碼,能夠快速提升我們的編碼水平,以及對設計模式有更深的理解。

    同時,在我們閱讀完一個原始碼後,可以觸類旁通,能夠快速地對其他框架的原始碼進行閱讀和學習,減少時間成本。

    除了上述提到的原因之外,可能還有許多,在這裡就不一一贅述了,那麼在確定了要閱讀原始碼之後,就讓我們看下如何閱讀原始碼吧!

    如何閱讀原始碼?

    如何閱讀原始碼取決於你為什麼要讀原始碼,比如:

    如果為了應對面試,那就可以圍繞常考的基礎類、集合類、佇列、執行緒、鎖等內容進行閱讀和學習;如果是為了解決 bug,那麼就可以只圍繞出現問題的相關類進行閱讀分析,隨著解決 bug 的增多,我相信閱讀的原始碼也會越多,從而更容易去閱讀和學習原始碼;

    下面大概說下閱讀原始碼的幾點建議:

    在閱讀之前,可以先從開源專案的官網上看它的架構設計和功能文件,瞭解這個專案的整體架構、模組組成以及各個模組之間的聯絡

    如果沒有對應的專案文件,可以根據程式碼的模組進行梳理,以形成對專案的初步瞭解,或者檢視已有的原始碼解析文章或者書籍,在閱讀原始碼之前,瞭解專案的架構和思路會使閱讀原始碼事半功倍。

    在瞭解一個類的時候,可以使用 ctrl+F12 來檢視類中的成員變數和方法。

    可以透過 IDEA 的 Diagrams 功能去了解一個類的繼承關係。

    多打斷點除錯,斷點追蹤原始碼是很好的閱讀原始碼的方式,可以先透過 debug 瞭解下呼叫邏輯,都和哪些類有關聯,有大致瞭解後再透過 debug 瞭解整體程式碼的功能實現,各個類都起到了什麼作用,有沒有涉及到設計模式等。

    另外,優秀的開源專案中肯定會有許多地方應用到了設計模式,建議在閱讀原始碼之前,需要對常用的設計模式有大致的瞭解,不然閱讀原始碼的效率會大大降低。

    如果遇到讀不懂某部分原始碼的時候,可以先跳過,之後再回來看,如果屬於搞不懂這部分就茶不思飯不想的人,可以在網上找是否有該部分原始碼的解析或者文件,也可以自己透過原始碼註釋和測試用例去閱讀學習。

    一般優秀的開源專案都會有單元測試,可以透過對應類的單元測試去了解方法的含義和用法,加深對原始碼邏輯的理解。

    在閱讀原始碼的時候,可以在程式碼上加上註釋和總結,同時還可以畫出時序圖和類圖,這樣對閱讀原始碼有很大的幫助,可以很清楚地知道類之間的呼叫關係和依賴關係,也方便以後回顧,重新閱讀。

    在這裡推薦大家一個 IDEA 外掛 SequenceDiagram,可以根據原始碼生成呼叫時序圖,便於閱讀原始碼。

    剛開始閱讀原始碼,不建議直接看框架原始碼,可以先從 jdk 原始碼看起:

    jdk 原始碼也是非常龐大的,可以分模組來閱讀,下面是建議的閱讀順序:

    java.lang 包下的基本包裝類(Integer、Long、Double、Float 等),還有字串相關類(String、StringBuffer、StringBuilder 等)、常用類(Object、Exception、Thread、ThreadLocal 等)。java.lang.ref 包下的引用類(WeakReference、SoftReference 等)java.lang.annotation 包下的註解的相關類java.lang.reflect 包下的反射的相關類java.util 包下為一些工具類,主要由各種容器和集合類(Map、Set、List 等)java.util.concurrent 為併發包,主要是原子類、鎖以及併發工具類java.io 和 java.nio 可以結合著看java.time 主要包含時間相關的類,可以學習下 Java 8 新增的幾個java.net 包下為網路通訊相關的類,可以閱讀下 Socket 和 HTTPClient 相關程式碼

    其他包下的程式碼也可以做下了解,JDK原始碼閱讀筆記:https://github.com/wupeixuan/JDKSourceCode1.8

    再有了一定的原始碼閱讀經驗後,可以再去學習 Spring、Spring Boot、Dubbo、Spring Cloud 等框架的原始碼。

    總結

    主要介紹了為什麼讀原始碼以及如何讀原始碼,供大家參考,每個人都有適合自己的閱讀原始碼的方式,希望可以在學習中去摸索出一套屬於自己的方式。

  • 2 # 大前端說

    沒看過原始碼,都不好意思出來說了,最近剛好在看一些,來說一個。

    先看使用 https://element.eleme.cn/#/zh-CN/component/installation

    先看一下這個庫是做什麼用的,然後提供了哪些功能。

    看GitHub https://github.com/elemefe再看程式碼

    clone 一份到本地,然後先看下目錄結構,然後根據文件看幾個簡單的元件的時候,一邊看掘金上的分析,一邊自己看下實現。

    e le

    餓了麼這個框架程式碼結構還是很清楚的,基本上每個元件都是分開的,所以你只要看其他的一個資料夾就行。然後一些工具的都在src資料夾。

    要學會看issue,一般開源的專案都有人會來提建議,有些是bug,有些是功能,你可以看看自己是否有能力去解決,如果可以的話,你可以去fork程式碼,然後自己修改,再提pr。

  • 3 # 聊網際網路金融科技的Li

    帶著問題去尋找問題的解答方式,多使用全文搜尋功能,查詢方法名,函式名和聯絡上下文關係進行逐點分析閱讀,切記當做小說來閱讀,程式碼是技術乾貨。透過我個人經驗總結得出以下兩個重點希望可以提供你們閱讀原始碼的效率。

    細節最重要

    如果是通篇閱朗,這樣等同於看小說一下,走的是故事的情節,卻不知道細節是如何發生,過流水賬的閱讀方式是低效又無用。

    程式碼不等同於文章

    原始碼不等同於文章,沒有文采飛揚的文字和情節描述,唯有知識乾貨和答案在等待你的發現,個人發現倒序的閱讀原始碼效果過更好,帶著結果層層往上尋找,你會發現其中的聯絡都根,枝,葉的關係,順瓜摸藤很適合程式碼的閱讀。

    總結

    原始碼其實就是別人的一個開發很成熟的程式,從實現的結果中層層拋開實現的原理和方法就是高效閱讀程式碼的方式,程式碼和小說閱讀的區別就是在於,目的性的最大差異,小說只是順著順藤摸瓜,程式碼是吃著瓜卻要思考著這個瓜是怎麼種出來的。

  • 4 # 不會飛的蝸牛

    1、需要過硬的基礎知識,這個前提。不然基本語法、常用的模式都不曉得怎麼讀。

    2、多參考歷史版本和更新變化,好的原始碼都是反覆迭代出來的精華,開始就讀精華是很不明智的,所以看看版本更新說明,版本的歷史演變。就想人一樣是怎樣進化過來的。

    3、參考別人閱讀註釋,想必在你讀原始碼之前也有人讀過了原始碼,並且總結,註釋。和分享原理,可供你參考,畢竟每個人讀一篇文章,理解的東西是有差異化的。

    4、直接買書,有些作品直接出書就是原始碼精解

    5、找個大神給你慢慢分析,這個最快。娓娓道來,直接面授比啥都強。缺點是,你容易跟著他的思維走下去。

  • 5 # 明月隨想

    1、一邊閱讀程式碼一邊寫註釋。這是我用過的最好的方法,對程式碼理解得更深入,看一些重要程式碼或者特別難懂的程式碼時挺有用。更何況,註釋也是一種文件嘛。

    2、一邊閱讀程式碼一邊繪製UML。這個方法適用於類之間的關係較複雜和呼叫層次較深的情況,我一般都是先繪製順序圖,然後為順序圖中的類繪製關係圖。

    3、透過Debug來跟蹤程式的主要執行過程,這樣就可以分清主次了,閱讀的時候更有針對性。

    4、類的快速閱讀。先弄清楚它在繼承鏈中的位置,看看它的內部狀態,也就是成員變數,一般來說,類的對外介面都是對成員變數的訪問、加工、代理等,然後看看它的對外介面,也就是公有成員函式,識別核心的一個或多個函式,這時候你應該可以大概瞭解這個類的職責或作用了。可能這個類是某個設計模式中的一個組成部分,所以,設計模式的掌握對程式碼的快速閱讀也是很有幫助的。

    5、帶著問題去閱讀。比如想了解android中的訊息機制,那麼看看Looper、Handler、MessegeQueue這幾個類就可以了,其他的不要去看,要不然就跑題了。

    下面列幾個閱讀原始碼時所處的情景,在特定場景下用哪些方法: 不太熟悉業務邏輯,還不是很清楚它是幹啥的,可以用3、5。 程式碼量很大,有幾十萬行,甚至百萬行,可以用2、3、5。 你無法看見程式的執行過程,比如沒有使用者介面,也有可能是無法執行的,可以用3、5。 設計複雜,用了大量的設計模式,呼叫鏈很深,可以用1、2、3、4、5。 時間有限,沒有那麼多時間讓你看原始碼,可以用3、5。

  • 6 # 架構思維

    下面是之前寫的一篇文章:《如何快速閱讀原始碼》

    本文探討在需要了解一個開源專案時,如何快速的理清開源專案的程式碼邏輯!

    以下是個人認為行之有效的方法:

    先「跑起來」自頂向下拆解深入細節延伸改進

    本文以Mybatis為例來進行演示!

    先“跑起來”

    程式界有個老傳統,學習新技術時都是從「Hello World」開始的!無論是學習新語言時,列印「Hello World」;還是學習新框架時編寫個demo!那為什麼這裡的「跑起來」要打個引號呢?

    實際上,當你想要閱讀一個開源專案的原始碼時,絕大部分情況下,你已經能夠使用這個開源專案了!所以這裡的“跑起來”就不是寫個「Hello World」,也不是能跑起來的程式了!而是能__在你的腦子裡「跑起來」__!什麼意思?

    Mybatis你會用了吧?那麼請問Mybatis是如何執行的呢?仔細想想,你能否用完整的語句把它描述出來?

    這裡是Mybatis的官方入門文章!你是如何看這篇文章的?讀一遍就行了嗎?還是跟著文章跑一遍就夠了嗎?從這篇文章裡你能獲得多少資訊?

    我們來理一下:

    安裝如何在專案中引入Mybatis?Mybatis的groupId是什麼?artifactId又是什麼?目前最新版本是多少?從 XML 中構建 SqlSessionFactorySqlSessionFactoryBuilder可以透過xml或者Configuration來構建SqlSessionFactory,那是如何構建的呢?xml配置了哪些資訊?既然使用了xml,那肯定有xml解析,用什麼方式解析的?xml裡的標籤都是什麼意思:configuration,environments,transactionManager,dataSource,mappers。以及這些標籤的屬性分別是什麼意思?SqlSessionFactory的作用是什麼?不使用 XML 構建 SqlSessionFactoryBlogDataSourceFactory,DataSource,TransactionFactory,Environment,Configuration這些類的作用是什麼?*Mapper的作用是什麼?為什麼提供基於XML和Java的兩種配置方式?這兩種配置方式的優缺點是什麼?從 SqlSessionFactory 中獲取 SqlSessionSqlSession的作用是什麼?selectOne和getMapper的執行方式有什麼區別?探究已對映的 SQL 語句*Mapper.xml的配置是什麼?名稱空間,id的作用是什麼?*Mapper.xml是如何和*Mapper.java進行匹配的?匹配規則是什麼?基於註解的對映配置如何使用?為什麼提供基於XML和基於註解的兩種對映配置?有什麼優劣?作用域(Scope)和生命週期SqlSessionFactoryBuilder應該在哪個作用域使用?為什麼?SqlSessionFactory應該在哪個作用域使用?為什麼?SqlSession應該在哪個作用域使用?為什麼?Mapper例項應該在哪個作用域使用?為什麼?

    回答出了上面這些問題!你也就基本能在腦子裡把Mybatis「跑起來」了!之後,你才能正真的開始閱讀原始碼!

    當你能把一個開源專案「跑起來」後,實際上你就有了對開源專案最初步的瞭解了!就像「書的索引」一樣!基於這個索引,我們一步步的進行拆解,來細化出下一層的結構和流程,期間可能需要深入技術細節,考量實現,考慮是否有更好的實現方案!也就是說後面的三步並不是線性的,而是__不斷交替執行__的一個過程!最終就形成一個完整的原始碼執行流程!

    自頂向下拆解

    繼續透過Mybatis來演示(限於篇幅,我只演示一個大概流程)!我們現在已經有了一個大概的流程了:

    SqlSessionFactoryBuilder透過xml或者Configuration構建出SqlSessionFactory可以從SqlSessionFactory中獲取SqlSessionSqlSession則是真正執行sql的類

    雖說每個點都可以往下細化,但是也分個輕重緩急!

    我們是先了解怎麼構建SqlSessionFactory呢?還是瞭解如何獲取SqlSession呢?還是瞭解SqlSession如何執行sql的呢?

    很明顯,SqlSession去執行 sql才是Mybatis的核心!我們先從這個點入手!

    首先,你當然得先下載Mybatis的原始碼了(請自行下載)!

    我們直接去看SqlSession!它是個介面,裡面有一堆執行sql的方法!

    這裡只列出了一部分方法:

    SqlSession就是透過這些方法來執行sql的!我們直接看我們常用的,也是Mybatis推薦的用法,就是基於Mapper的執行!也就是說「SqlSession透過Mapper來執行具體的sql」!上面的流程也就細化成了:

    SqlSessionFactoryBuilder透過xml或者Configuration構建出SqlSessionFactory可以從SqlSessionFactory中獲取SqlSessionSqlSession則是真正執行sql的類SqlSession獲取對應的Mapper例項Mapper例項來執行相應的sql

    那SqlSession是如何獲取Mapper的呢?Mapper又是如何執行sql的呢?

    深入細節

    我們來看SqlSession的實現!SqlSession有兩個實現類SqlSessionManager和DefaultSqlSession!透過IDE的引用功能可以檢視兩個類的使用情況。你會發現SqlSessionManager實際並沒有使用!而DefaultSqlSession是透過DefaultSqlSessionFactory構建的!所以我們來看DefaultSqlSession是如何構建Mapper的!

    它直接委託給了Configuration的getMapper方法!

    Configuration又委託給了MapperRegistry類的getMapper方法!

    在MapperRegistry類的getMapper中:

    透過type從knownMappers中獲取對應的MapperProxyFactory例項如果不存在則丟擲異常如果存在則呼叫mapperProxyFactory.newInstance(sqlSession)建立對應的Mapper

    在這裡knowMappers是什麼?MapperProxyFactory又是什麼?mapperProxyFactory.newInstance(sqlSession)具體做了什麼?

    其實很簡單,knowMappers是個Map,裡面包含了class與對應的MapperProxyFactory的對應關係!MapperProxyFactory透過newInstance來構建對應的Mapper(實際上是Mapper的代理)!

    快接近真相了,看mapperProxyFactory.newInstance(sqlSession)裡的程式碼:

    這裡幹了什麼?

    透過sqlSession,mapperInterface和methodCache構建了一個MapperProxy物件然後透過Java的動態代理,來生成了Mapper的代理類將Mapper方法的執行都委託給了MapperProxy去執行如果是Object裡的方法則直接執行否則執行MapperMethod的execute方法

    最終實際還是委託給了sqlSession去執行具體的sql!後面具體怎麼實現的就自行檢視吧!

    延伸改進

    現在我們的流程大概是這樣的一個過程:

    SqlSessionFactoryBuilder透過xml或者Configuration構建出SqlSessionFactory可以從SqlSessionFactory中獲取SqlSessionSqlSession則是真正執行sql的類SqlSession獲取對應的Mapper例項DefaultSqlSession.getMapperConfiguration.getMapperMapperRegistry.getMappermapperProxyFactory.newInstance(sqlSession)透過sqlSession,mapperInterface和methodCache構建了一個MapperProxy物件然後透過Java的動態代理,來生成了Mapper的代理類Mapper例項來執行相應的sql將Mapper方法的執行都委託給了MapperProxy去執行如果是Object裡的方法則直接執行否則執行MapperMethod的execute方法最終還是委託給sqlSession去執行sql

    現在我們大概知道了:

    為什麼Mapper是個介面了Mybatis基於這個介面做了什麼

    那麼,

    什麼是動態代理(基礎哦)?為什麼使用動態代理來處理?基於動態代理有什麼優點?又有什麼缺點?除了動態代理,還有其它什麼實現方式嗎?比如說cglib?如果是其它語言的話,有沒有什麼好的實現方式呢?......

    這個問題列表可以很長,可以按個人需要去思考並嘗試回答!可能最終這些問題已經和開源專案本身沒有什麼關係了!但是你思考後的收穫要比看原始碼本身要多得多!

    再迴圈

    一輪結束後,可以再次進行:

    自頂向下拆解深入細節延伸改進

    不斷的拆解->深入->改進,最終你能__透過一個開源專案,學習到遠比開源專案本身多得多的知識__!

    最重要的是,你的流程是完整的。無論是最初的大致流程:

    SqlSessionFactoryBuilder透過xml或者Configuration構建出SqlSessionFactory可以從SqlSessionFactory中獲取SqlSessionSqlSession則是真正執行sql的類

    還是到最終深入的細枝末節,都是個完整的流程!

    這樣的好處是,你的時間能自由控制:

    你是要花個半天時間,瞭解大致流程還是花個幾天理解細節流程還是花個幾周,幾個月來深入思考,不斷延伸你都可以從之前的流程中快速進行下去!

    而不像debug那樣的方式,需要一下子花費很長的時間去一步步的理流程,費時費力、收效很小,而且如果中斷了就很難繼續了!

    總結

    本文透過梳理Mybatis原始碼的一個簡單流程,來講述一個個人認為比較好的閱讀原始碼的方式,並闡述此方法與傳統debug方式相比的優勢。

  • 7 # 自信中國人上海程式設計師

    我最近恰好找摸索出一個梳理遺留系統架構的技巧:自底向上 找到一個典型的切面 沿著呼叫和回撥的路徑 在程式碼中新增結構化註釋(比如eclipse中加//TAG 流程A1.1 甲->>乙),這樣便得到了一個code地圖,並且在tasks檢視中看起來很直觀(看起來跟書的目錄一樣)可快速跳轉。將目錄copy到有道雲筆記的markdown序列圖中 就自動生成了一個序列圖。

    我覺得這基本上就是可縮放的視覺化架構地圖了,對維護一個比較亂和龐大的遺留系統非常有幫助,定位程式碼 修改維護都方便多了。

  • 8 # 擼貓撩妹任逍遙

    很簡單,帶著疑問和好奇心,這是最根本的!我當初學微機原理的時候,一直都在思考一個問題,那就是電腦到底是如何思考的呢?然後就學得非常深入和透徹。

  • 9 # 小莊讀書

    以個人經驗來看,高效閱讀原始碼,最重要的一點就是要避免“只見樹木、不見森林”、陷入細節不能自拔……

    所有的原始碼,不論是什麼程式語言C、C++、Java、Python;不論是什麼程式設計模式:面向物件、面向過程、模板程式設計、函式程式設計——原始碼的結構其實都是一個一個、一層一層的函式呼叫……。

    誠然,要讀懂一份原始碼,必然要讀懂其中的每一個函式。但是,在讀懂這些函式之前,在腦海中要時刻保持清醒的一點是,原始碼都是有自己特定的組織結構的,也就是每一個函式呼叫、每一層函式呼叫都是有內在的邏輯關聯的,每一個、每一層函式呼叫都不是孤立的……

    因此,當你在閱讀一份原始碼的時候,在你一個一個認真分析每一個函式的功能的時候,一定要時刻記住上層程式碼是如何一層一層呼叫每一個函式的,也就是要時刻關注程式碼的組織結構,避免陷入某個函式的實現細節當中,而忽略了整個原始碼的邏輯結構,最後導致看了一堆各種各樣的函式,卻沒有弄清楚整份原始碼的結構、功能是什麼……

    最後,還有重要的一點是,當分析某個函式時,如果一時不能完全理解其中的細節的時候,其實完全可以先跳過這個函式,待理清楚整個原始碼的邏輯結構之後,也許那個函式的種種細節就能夠自然而然地理解清楚了。

  • 中秋節和大豐收的關聯?
  • 明日方舟無氪玩家一枚,最近黃票攢到300了,現在非常糾結換安潔莉娜還是繼續等艾雅法拉進黃票商店?