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