ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[toc] 如果您喜欢这份小册,麻烦您分享给您的朋友哈。 ## 一、如何阅读源码? 优质的开源项目,不论是从业务概念上的抽象,还是实实在在、反反复复的调优、验证,很可能是凝聚了一个团队几年的心血,甚至有着庞大的体系。 开源的项目已经为软件生态注入了强大的血液。一个优秀的团队,务必有这样的先行者——他们无惧未知、勇于先行,在缥缈的线索中逐渐抽丝剥茧、持续深耕,学习过往经验、承前而启后。这个过程中,搜集资料、解读开源,便是一门程序员的必修课。 那我们如何有效的阅读源码呢? ### 源码解读基本流程 在总结源码解读思路之前,不妨先想一想我们是如何构建一个项目的呢?粗略的讲,大概是这样的步骤吧: ![](https://img.kancloud.cn/99/72/99723fdea06d97c13d9913ddb1372a33_776x293.png) 那么,解读源码的时候,其步骤也大体类似,只不过写代码的人不是自己罢了。根据以往源码阅读的经验,我们大概可以总结出这样一条经验: 1. 把握模块划分; 2. 通过配置深入了解软件功能; 3. 解读配置加载流程; 4. 提出核心问题,见招拆招~ ## 二、高效阅读心法 ### 把握核心结构 越是优秀的开源项目,其在模块设计上更加注重解耦,各个模块各司其职,才得以组成一个可持续维护的项目。阅读源码前,不防看一下系统提供的架构图,对各个模块的职责有一个基本的了解,并能基本理解作者对模块划分的依据。 ### 站在巨人的肩膀上 今天你所遇到的问题,早在很久之前就有先驱者们探索过了。所以,我们一定要利用好互联网上的资源,为自己将要涉足的这片“土地”有基本的了解。甚至可以先参考一下别人对这款软件源码的解读,以便自己更好地找到学习的方向。 如果没有,那你就是先驱者啦!我们也可以留下自己的脚印供后来人欣赏、借鉴~ ### 提出问题,找寻问题答案 这样可以带有目的性地来阅读源码,效果往往会比较好。逐渐地,我们也会形成自己的一套提问的方法论。比如我会这样提问: - 先读配置文件,了解配置文件核心参数的结构和含义。配置文件的结构凝聚的便是软件功能的核心结构;接下来再去了解各个参数背后的含义,这样我们便对该软件基础功能有了较为深入理解。 - 找出系统启动的入口。 ## 三、代码精读:踏上调试之旅 “**调试是源码阅读的最好方式**”,这句话放在源码学习的过程中一点都不为过。 调试过程中,我们不仅可以结合业务操作对代码入口的进行梳理。 另外,在了解业务的基础之上,通过调试能够让我们直观地看到程序在运行中的数据,这样我们就可以更加深入了解作者的代码设计和开发思路。 ### 3-1 调试法思路 #### 倒序法 调试一般用到的源码阅读方法就是倒序法。熟悉Java堆栈日志信息的同学,很容易就能理解,问题一般出在**栈顶**的代码里。这里要么有异常、要么有一条显眼的日志。通过这里,我们再查看堆栈信息,就可以把握程序的执行逻辑了! 通过倒序法,我们可以精准打出一系列的断点。最终我们还需要配合正序法,完成逻辑的串联。 #### 正序法 正序法是用来把倒序法标记的源码断点串联起来。配合功能测试,我们按照正序的方法的理解源码的真正的执行逻辑。 ### 3-2 有所痕迹 好记性不如烂笔头。再强的理解能力,都比不上沉淀。我学习源码的时候,一般会通过画图或者笔记的方式把走过的路记录下来——它们好似我的脚印,等我回头眺望的时候,总能够给我以明确的指示。 #### 画图 用图说代码是一个学习源码的不错的方式:一来可以清晰剖析代码结构,二来通过记录源码关键位置,回过头来再看时节省时间,且有迹可循;三来可视化效果比较好,降低沟通成本~ 接下来,我们查看一个Dolphinscheduler源码解读的案例。 >[danger] 阅读源码的同学可以移步:https://www.toutiao.com/i7025583458269479454/ ![](https://img.kancloud.cn/18/64/1864116c85c96ad4fead4aeb404e4b4d_1024x281.png) #### 记录关键的堆栈信息 堆栈信息就是代码森林里面的坐标,记住这个坐标,我们便不再迷路。 ### 3-3 掌握原理要看数据结构 举个过往的例子,刚开始学习Netty的时候,总是会纠结于pipeline中的handler的执行顺序,生怕放错了顺序,导致了错误的执行。因为在学习Netty的时候,教程中并没有提出出站处理器和入站处理器的数据结构,而是云云了一大堆所谓的规则,诸如: - 前面的入站处理器需要调用super.channelRead(ctx, msg)或者ctc.fireChannelRead(msg),消息才能向后方的入站处理器传递——所谓,“击鼓传花,不能有人偷懒”; - ChannelHandler的执行顺序:ChannelInboundHandlerAdapter是按添加顺序,ChannelInboundHandlerAdapter则与添加顺序相反。 这些总结最多只能算是“规则”或“特征”,如果我们陷入了记忆规则的思维模式上,我相信,学习编码一定是个很累的过程。因为,再多的记忆也无法理解其本质。 还记得我们在中学时代就开始学习的数学中经常会提到两个概念吗——**定理**和**性质**。 **数学定理**:定理是指在既有命题的基础上证明出来的命题,这些既有命题可以是别的定理,或者广为接受的陈述,比如公理。 **数学性质**:是数学表观和内在所具有的特征,一种事物区别于其他事物的属性。 子涵认为,**定理更加偏向原理,性质更加偏向特征**。我们应该把握原理,因为通过原理来推断性质,是一个水到渠成的过程。在学习编程的过程中,要想把握原理,就需要掌握其数据结构。 ![](https://img.kancloud.cn/ca/71/ca71918ae1b6fdb921de266f03d5f552_989x253.png) Netty消息处理的pipeline各个其实是一个“双向链表”结构:读消息的过程,就是next指针向后轮询的过程,当该处理器是入站处理器时,执行读操作;写消息的过程,则正好相反。 我相信,把握了这个结构,我相信聪明的你一定可以随心所欲地定义Handler的顺序了。 >[danger] 转载请注明出处~