Skip to content

昨天谈到我最近一直在看一些jdk的源码实现,也算是略有小得。最近正好组内组织会议,我有幸做了一下关于阅读源码的心得体会的讲演,下面就是我在这次讲演中用到的演讲稿,这不又能水一篇了。哈哈哈哈。。。

进入正文了哈。。。

其实你一直在看源码,可能你没意识到,你看别人的代码不就是源码?

你新入职一个公司要熟悉代码的时候不就是看源码?

不管是份内工作开发所需要的代码,还是开源框架源码没有本质的区别,要真说区别无非是代码的质量、整体的设计区别罢了。

所以没啥好怕的,不要被劝退了。

一.源码阅读的困难点

刚开始学习项目源码对绝大部分小伙伴们来说应该是偏难的。可能有以下四点原因:

1. 不管是成熟的开源项目还是工作内开发的项目产品,必定功能齐全,可扩展,而功能齐全可扩展的开源项目必定很复杂,代码量大。比如 Spring5 框架的源码行数达到了六七十万行,我们的核算ACAS系统六百万行代码,可以想象其有多复杂。

2. 阅读源码时,我们有时候无法猜透源码作者当时编码时的想法。因为在刚开始阅读源码的过程中,我们肯定会遇到很不懂的代码,不知道作者为何这么写,这些都是很正常的,因为当初作者可能是针对一些比较特殊的业务场景,或者为了某方面的性能等等,我们根本无法猜透。

3. 有些源码可能集操作系统知识,数据结构,算法和设计模式于一身。优秀的代码必定是多种优点的集大成者。比如很多框架运用了单例模式,工厂模式,责任链模式,装饰器模式和模板方法模式等。同时,优秀框架或者项目代码也会借鉴一些很优秀的范例,但是限于我们的认知范围,难以理解这一部分代码。比如, Dubbo 中的RoundRobinLoadBalance 最终借鉴了 Nginx 的 RoundRobinLoadBalance 负载均衡算法。总之这里要说明的是阅读优秀框架是有一定难度的。

4. 源码注释太少也增加了阅读源码的难度。开源项目的注释,一般都会有大量注释,比如 Spring 框架,可以说几乎每个方法都有注释,这个就给我们阅读源码起了很大的帮助。不过唯一不好的可能就是英文注释,阅读对英语有一定的要求。其实比较头疼的就是一些国内优秀的开源框架,甚至是我们产品代码,其注释可以说是很少的,这无疑大大增加了阅读的难度,尤其像我们的系统,很多需求文档由于公司原因或者本身需求交接,导致文档也不齐全,那就更加 GG 了。

二.工作内的源码阅读

我先分享一下我入职一家新公司接手项目的时候是怎么做的。入职新公司接手项目就是在读源码

第一招:项目碰头会

新入职接手项目的时候,邀请产品或者项目经理和原先这个项目的主力开发开了个会。

这个会的目的就是让产品项目经理介绍一下这个项目的背景、要解决什么问题、有哪些功能。

开发在旁边补充、解答我的疑惑,毕竟产品经理不太了解细节上的数据交互。

这个会议下来你就能得知这个项目到底是干嘛的,能提供哪些功能。

业务上的理解对你之后读源码非常的重要!!

第二招:文档百科书

然后就是去扒,去要,软磨硬泡,所有涉及到的文档、架构图、流程图、时序图,关系图等等(有多少要多少,没的话没办法)。

站在项目碰头会的肩膀上,看完所有的文档加深对整个项目的了解。

第三招:run起来

紧接着,最重要的是你要让整个项目跑起来,跑起来之后,开始用这个软件,各种功能点一点,毕竟听产品经理说和自己实际用还是有区别的。

然后基本上点过项目的各个主体流程之后,整个项目你都会有一个比较全面的理解。

第四招:死磕源码

这个时候看源码,查看源码的目录或者简单的看文件的命名其实已经能知道这个文件对应着哪个模块了,有种胸有成竹的感觉。

当然代码毕竟不是小说,从头到尾通读的方式还是会有很多的困难。这个时候就需要你拿出debug的手段,慢慢死磕。毕竟Java是面向对象的语言,由于多态的存在,可能很多时候光看代码不知道具体选择哪一个子类。这时候阅读与调试双剑合璧,就是一力破万法的关键。

第五招:以小见大

开始接活吧,小同志。纸上得来终觉浅,绝知此事要躬行。具体的深入细节就要结合分配到的任务了,几个需求接下来渐渐地以小见大,成竹于胸。。

所以总结来说入职接手项目就是需要了解背景、总览全局然后再细化的反复螺旋向上的一个过程。

三.工作外的源码阅读

刚参加工作那会,没想过去读源码,总想着别人的框架应该是完美的、万能的,应该不需要改。

工作了一年多之后准备跳槽了,开始了一轮的面试,其中有几个面试官就问到了相关的源码问题,那时候开始意识到,源码这东西在之前的工作的中感受不到,但是在面试中好像面的还挺频繁的,从此有意识的开始了部分源码的阅读。

源码学习前的准备:

内容了解

最好的方式就是官方参考指南,亲生父母往往对孩子是最了解的,对孩子的描述也是最详细的

其次是书籍,国外优秀的有很多,国内也不乏好书,比较推荐此方式,自成体系,让我们掌握的知识点不至于太散。这就是好比是源码的闺蜜,对源码非常了解,重点是挺大方,会尽全力帮助我们了解源码。

再次就是博客,虽然可能觉得知识点比较散,但是针对某个知识点却特别的细,对彻底掌握非常有帮助,园子内就有很多技术大牛,这就是源码的朋友圈,我们从中也能获取到非常多关于源码的信息。

设计模式的了解

优秀的框架、技术从不乏设计模式;jdk源码中就应用了很多设计模式,比如IO流中的装饰模式、GUI的观察者模式、集合中的迭代器模式等等;我们对一些常用的设计模式有个大致了解,再去读源码是比较好的,设计模式就像是八股文,是一种通用的格式,套路。我们需要好好掌握设计模式这个套路。

掌握基本的ide调试手段进行断点追踪

面对未知的、茫茫多的源码,我们往往没有足够的时间、经历和耐心去通读所有源码,我们只需要去读我们关注的部分即可。明确我们的目的,找到合适的切入点,进入断点调试追踪也就容易了。

如何阅读开源项目的源码

读源码我个人分为两种情况:为了提升自己和为了找问题。

为了提升自己而读源码

我默认你是知道你要看的开源项目是干嘛的,比如 RocketMQ 是消息队列,消息队列是干嘛的你应该先知道。

我也默认你用过这个开源项目,业务上没用过自己私下也要先用用,了解简单功能怎么用,让它先跑起来。

同样的套路:

1.首先看官网、wiki。

2.然后看源码目录,你得先知道每个目录是干嘛的涉及哪些功能,这其实和你看业务源码一样。

3.然后就是找突破口了,这种开源项目都有 demo ,跑,打断点就完事儿了!

这就是突破口。

4.然后就开始源码之路了,是的还是得自己啃,硬啃,这是读源码的必经之路!

读源码有时候会觉得代码很多,分支好多。

没事,先拷贝一份,然后把一些异常处理和不常见的分支先删了。

整体核心流程先理清楚!

并且理清楚了一个流程之后开始画图,流程图、脑图都上。

清楚之后再看没删减的代码,把异常处理的一些也理解了,补充完整流程图、脑图等。

然后这一模块就收工了!搞定!

然后各种分支发散出去,大致的流程就都清晰了,源码也就读的差不多了。

读源码的时候也会遇到一些不能理解的,先略过,主流程先搞懂。

搞懂整体核心流程之后可以抠一些细节了,而细节往往就是我们需要学习的地方,所以在理清整体流程之后不要错过细节。

往往你觉得很奇怪的地方可能就是一些“骚操作”,学的就是“骚操作”。

为了找问题而读源码

这个目的性很强,有时候是项目出错,一般而言有日志,所以通过日志搜就行。

如果你本身对这个框架很熟悉那当然最好,如果不熟悉通过日志搜索结合上下文其实也能找到一些缘由。

不过有时候还是得整个链路分析下来才能排查问题,这个看功力了。

有时候是因为看到一些文章的说法冲突了,一篇说 A 另一篇说 B ,如果你找不到权威的信息你只能自己去看源码,通过关键字搜。

源码之下无秘密。这种目的性很强的读源码就得结合当时上下文和靠个人功力了。

最后,我们阅读源码要注意一些技巧,现在根据自身经历总结一下相关思路和技巧,如下:

(1)开始阅读源码时,先对框架的模块及其关系有一个整体的认识。我们要对框架项目的模块和目录要有一个全盘的了解,要知道每个模块是干嘛的,然后要了解模块与模块之间的关系。

(2)分析源码先从父类或父接口开始分析。因为父类或者父接口往往代表了一类功能,这些基类或基类接口往往抽象了各个具体子类共有的属性和行为,一些比较基础的方法都在父类中实现,然后留个模板方法给子类去实现即可

(3)阅读源码前首先要找到启动类。阅读分析源码时要先从启动类开始,因此找到启动的入口很重要。

(4)阅读源码时要分清主干和枝节代码。找到启动入口后,然后就可以顺着启动入口一步一步调试来阅读源码了。不过在初次调试源码时值得注意的是一定要分清主次代码,即要先阅读主干代码,其他枝枝节节的代码没明白的可以放一边。切忌一开始就深入细节然后出不来了,这样就会造成只见冰山一角而看不到全貌的感觉。

(5)阅读源码前要分清主次模块。即阅读分析源码不能漫无目的,全盘通读,我们要从我们平时有用到的模块开始分析。

(6)要充分利用源码项目的测试类。之前也说过,一个框架之所以能流行,必定是经过大量测试的。因此如果我们想具体了解某个类和某个方法,我们可以充分利用这些测试类来辅助我们源码分析。

(7)要学会一些调试技巧。这一点也很重要,比如在调试过程中如何查看调用关系等等

从上至下全部通读的方式,个人不太推荐,这是建立在很熟悉的基础上的,当我们对某个框架已经比较熟悉了,再从上至下进行通读,彻底了解,这是我认为正确的方式;但是从不熟悉到熟悉这个过程,个人不推荐全部通读,而是推荐断点局部追踪。重要的是开始,慢慢的就会形成自己的一套读源码的方式;每个人的方式都不一样,合适自己的才是最好的。

源码阅读的三层境界

王国维在《人间词话》中曾说过治学三境界,

昨夜西风凋碧树,独上高楼,望尽天涯路

衣带渐宽终不悔,为伊消得人憔悴

众里寻她千百度,蓦然回首,那人却在灯火阑珊处

同样,我认为阅读源码也有着三层境界:

初级:记流水账

初期的源码阅读文章基本上是记流水账,例如对源码一行行加注释,只关注底层实现细节,但并未形成更高层次认知,对其设计理念没有提炼与深度领悟。

中级:能提问、思考、提炼

要求我们在阅读源码的时候多思考,并反问自己如果自己实现的话该如何着手,如何设计,带着疑问去研究源码。通过对比,思考,会对其背后的理念有了更深刻的理解。

高级:思考、质疑、验证

不管是什么代码,都会存在BUG或者实现并不合理的地方,如果大家在阅读源码的时候能够深入思考, 合理质疑,并能通过验证证明自己的观点,说明我们的能力、思考得到了极大的提升。

最后,我们学习源码不是为了学习而学习,最理想的效果我们要学以致用。比如把从源码中学习到的设计模式,接口设计方法,面向对象原则和相关算法等等都可以应用到我们手头的项目中,这才是我们学习源码的最终目的,也是源码学习的最理想的效果。可能这里有些小伙伴会说,我平时参与的项目都是业务类的项目,而不是开发基础框架,开发中间件,CRUD 比较多,可能学习基础框架的源码对我们用处很少。其实不是的,只要你有参与项目,学习源码我们学习的是思想,我们就可以把源码框架设计中的思想应用到我们的项目中。

最后的最后,我们来谈谈坚持,这是最难能可贵的。最后送大家一句话,与小伙伴们共勉。

人一能之,己百之;人十能之,己千之。果能行此道,虽愚必明,虽柔必强

- 《礼记 中庸》

图片

Released under the MIT License.