程序员如何去阅读开源项目的源码?

张开发
2026/4/4 4:22:37 15 分钟阅读
程序员如何去阅读开源项目的源码?
这篇文章我准备来聊一聊如何去阅读开源项目的源码。在聊如何去阅读源码之前先来简单说一下为什么要去阅读源码大致可分为以下几点原因最直接的原因就是面试需要面试喜欢问源码读完源码才可以跟面试官battle提升自己的编程水平学习编程思想和和代码技巧熟悉技术实现细节提高设计能力...那么到底该如何去阅读源码呢这里我总结了18条心法助你修炼神功学好JDK身为一个Javaer不论要不要阅读开源项目源码都要学好JDK相关的技术。所有的Java类开源项目本质上其实就是利用JDK已有的类库和关键字实现一种业务功能所以学会了JDK相关的类库是看其它的源码基础。如果你不懂JDK你去阅读源码会发现有太多看不懂的地方会影响读源码的心情和信心。学习JDK主要包括使用和原理两部分。内容大致包括以下几部分:集合相关比如常见的MapListQueue的实现包括线程安全与不安全并发相关比如synchronized、volatile、CAS、AQS、锁、线程池、原子类等等io相关包括bio和nio等等反射相关网络编程相关...了解设计模式在一个优秀的开源项目中设计模式处处存在所以在你开始阅读源码之前最好先了解一下常见的一些设计模式。当你了解了一些设计模式以后在源码中遇到了相关的设计模式你就可以快速明白代码结构的设计从而以整体的视角去阅读相关代码。同时学习设计模式不仅可以帮助我们阅读源码在日常开发中也可以帮助我们设计出更易于扩展的程序。学习设计模式的话可以看看《大话设计模式》这本书如果不想看书也可以找一些视频或者专栏。之前我也写过一篇关于开源项目中常用的设计模式文章 两万字盘点那些被玩烂了的设计模式 有兴趣的小伙伴可以看看。先从官网入手官网是介绍开源项目的地方同时也是学习一个开源项目最开始的地方通过官网我们可以快速的了解项目比如项目的定位一些核心概念功能使用教程整体的架构和设计常见的问题及解答...RokcetMQ官网当你了解了项目的一些概念、功能等信息之后如果你在读源码一旦发现了代码是实现这些概念或者功能的足迹那么能够帮助你更好的理解代码。熟悉源码模块结构当你对项目有大致的了解之后就可以从Github上把代码clone下来官网有项目源码的Github地址。当成功拉下来代码之后就可以对项目源码模块进行简单的分析熟悉模块结构分析模块功能混个眼熟。如上是RocketMQ源码如果前面阅读过官网相关的一些概念介绍就大致可以知道这些模块有什么功能。RocketMQ概念介绍比如说源码中的broker模块官网说broker主要是负责消息存储那么broker模块代码块肯定就主要实现了消息存储的功能。还有些模块可以根据单词的意思进行判断比如common模块一看就是存储一些公共类的模块example模块就是RocketMQ使用代码示例的模块等等。顺着demo开始读有的小伙伴在读源码的时候不知道从哪里开始读比较合适最后随便从源码中的某个模块就开始读读读越来越发现读不下去。读源码正确的姿势应该是从demo开始读。比如说现在我想要阅读一下RocketMQ生产者是如何发送消息的整个过程是什么样的那么我首先至少得写个发送消息的demo看看代码是如何写的。demo一般可以从官网中查看RocketMQ官网发送消息代码示例除了官网一般开源项目在源码中也会有相应的demo代码放在示例模块就比如上面提到的RocketMQ的example模块。最后还可以通过谷歌搜索一下demo。DefaultMQProducer producer new DefaultMQProducer(sanyouProducer); //指定NameServer的地址 producer.setNamesrvAddr(localhost:9876); //启动生产者 producer.start(); //省略代码。。 Message msg new Message(sanyouTopic, TagA, 三友的java日记.getBytes(RemotingHelper.DEFAULT_CHARSET)); // 发送消息并得到消息的发送结果然后打印 SendResult sendResult producer.send(msg);如上是RocketMQ生产者发送消息的一个demo消息发送源码阅读就从这块代码开始入手一步一步进入源码中这就算开始阅读源码了。带着目的去读带着目的去读其实很好理解就拿上面生产者发送消息流程源码来说读源码的第一个目的其实就是弄懂生产者发送消息的流程。除了弄懂生产者发送消息你还可以带着其它目的去读。比如说消息发送的核心逻辑是send方法实现的那么除了消息发送是不是可以去弄懂生产者在启动的过程做了哪些事也就是start方法的作用。再比如生产者发送消息肯定涉及到网络通信相关的内容那么了解RocketMQ底层网络通信模型是不是也可以算一个目的。当你带着这些目的你读源码就有很强的目的性读完印象会很深刻。当然如果你最开始想不到这些目的也没有什么关系你可以先往下读在读的过程中再去尝试发现一些其它的目的。先抓主线再抓分支有的小伙伴在读源码的时候每个方法都使劲一直往下点最后都不知道代码进入到哪了这其实是非常不可取的。正确的方法应该是先抓住主线流程分支流程先大致看看知道大概是什么作用等读完主线之后再回过头仔细读一下分支代码。举个例子来说在Spring中ApplicationContext在使用之前需要调用一下refresh方法而refresh方法就定义了整个容器刷新的执行流程代码。refresh方法部分截图当在读这段代码你可以先读一读refresh中各个方法大致都做了什么等读完之后你可以具体的去读每个代码的具体实现比如说prepareRefresh干了什么obtainFreshBeanFactory是如何获取到BeanFactory的prepareBeanFactory又在对BeanFactory做了什么事等等。不要过度抠实现细节有的小伙伴在阅读的时候特别喜欢深究想要弄清每行代码是如何实现的这不仅非常难而且也是不可取的。就比如说我们都知道在Spring Bean的生命周期中当存在基于xml的方式来声明Bean的方式Spring会去解析xml生成BeanDefinition。当你想要了解Bean的生命周期过程的时候其实是没有太大的必要去过度扣Spring是如何解析xml生成BeanDefinition的细节这对你整体了解Bean的生命周期没有太大的意义只需要知道最终会转换成BeanDefinition就可以了。那什么时候去扣实现细节呢当你需要使用到的时候比如说你遇到了一个bug或者是需要扩展阻碍你理解功能实现的时候大胆猜读源码的时候也需要我们发挥一点想象力去猜一猜功能是如何实现的。猜不是瞎猜而是基于目前了解的一些知识、技术或者是思想合理地去猜。就比如说当你已经知道了OpenFeign最终会对每一个FeignClient接口生成动态代理对象之后注入的对象都是代理对象代理对象中实现了RPC的请求之后那么当你在学习dubbo的时候是不是就可以去猜测注入的dubbo接口最终也是一个动态代理对象并且这个代理对象也实现了RPC的请求之后你在读代码的时候就需要着重注意发现是否有动态代理生成的代码这就算是一个目的一旦发现了动态代理相关的代码那么这块代码很可能就是dubbo RPC实现的核心。学会看类名不要小看类名优秀的代码命名都是见名知意的所以从类名也可能窥探出这个类的一些蛛丝马迹。如下列举了几个比较常用的命名习惯以Registry结尾的一般都是存储功能比如Spring中的SingletonBeanRegistry就是用来保存单例Bean的Mybatis中的MapperRegistry就是用来保存Mapper接口的以Support、Helper、s、Util(s)结尾的一般都是工具类以FilterInterceptor结尾的一般都是拦截作用一般会配合责任链模式Chain使用以Event、Listener结尾的一般都是基于观察者模式实现的事件发布订阅模型...除了一些比较通用的命名习惯也有一些项目独有的一些命名习惯。比如说Spring中常见的以PostProcessor结尾的都是扩展接口实现这些接口可以拿到某个比较核心的组件从而实现对Spring的扩展。其实很多开源项目的命名都比较偏向Spring的命名风格当你遇到了跟Spring的命名比较像的时候那么可以大胆猜测类的作用。学会看类结构类结构也非常重要他也能够帮助我们窥探类的大致功能。ApplicationContext如上图是Spring中ApplicationContext的继承体系当你需要了解ApplicationContext的时候可以先去熟悉一下它的父接口的作用当你大致弄明白了每个接口的作用那么ApplicationContext有啥作用就大致就清楚了。除了可以看类继承体系还可以浏览一下类大致提供了哪些方法了解对外提供的功能。类方法通过快捷键 ctrlF12macfncommandF12查看并且还支持模糊搜索方法名我本人就非常喜欢这个快捷键ApplicationContext总结类的职责当我们在读完一个类的代码的时候一定要总结这个类的职责明白这个类存在的意义。一般情况下一个类核心职责只有一个遵循单一职责的设计原则。举个例子在RocketMQ中有一个类MQClientAPIImplMQClientAPIImpl其实从名字大概看不出这个类主要是有什么功能但是当我读代码的时候发现每个方法最终都调用RemotingClient方法而RemotingClient只有一个实现NettyRemotingClient所以从这个实现和类名可以猜出来RemotingClient是发送网络请求的客户端所以当读完MQClientAPIImpl源码之后我就知道了MQClientAPIImpl这个类的职责大致是封装参数然后通过RemotingClient向MQ发送消息的。当知道这个类的职责的时候那么其它地方在调用这个类的方法的时候就知道大概在做什么事了。习惯阅读注释当你在读源码的时候如果有注释最好能先读一下注释这样能帮助你厘清类或者方法的功能先知道功能再去读源码就容易多了。注释一般都是英文如果看不懂可以装个插件写好注释俗话说的好记性不如烂笔头写好注释也是阅读源码中很重要的一个环节好的注释可以帮助快速回忆起实现细节和功能。注释并不需要对每行代码都注释当然如果你愿意也没多大问题但是注释应包括以下几点内容核心类和方法实现的核心功能核心功能大致的实现逻辑核心的成员变量的作用方法中不易读懂的代码实现细节DefaultMessageStore如图是我读RocketMQ中对于DefaultMessageStore类阅读的注释这个类是RocketMQ中一个非常核心的类从名字可以看出来跟消息的存储有关。这个类的功能非常多所以我写了很多注释列举了这个类主要有哪些功能和这些功能实现的一些细节。总结思想及时输出当你读完某个功能模块的时候就可以尝试对这块功能实现逻辑或者思想进行总结。比如说当你了解了CAS思想的时候你会发现原来保证线程安全不仅仅可以通过加锁的方式还可以基于乐观锁的方式来实现。在总结之后可以输出成一个文档又或者是流程图。我个人比较喜欢画图这里推荐两个在线画图工具processondraw.ioprocesson我平时就在用功能多但是需要收费draw.io的话免费图标和颜色感觉比processon好看平时文章中的贴图就是用draw.io画的。这里多说一句总结思想还是非常重要的在我阅读了很多源码之后我发现很多技术或者功能的实现原理最终都是殊途同归。提前了解依赖的技术一般一个开源项目不是所有的技术都是自己实现的它也会依赖一些其它的框架或者是思想提前了解这些框架或者是思想可以帮助你更好地阅读和理清代码。比如说RocketMQ底层是基于Netty框架实现网络通信的当你对Netty有所了解知道Netty在启动的时候需要注册一堆ChannelHandler用来处理网络请求那么在读RocketMQ底层网络通信功能的时候你就可以去找一下Netty启动的代码看看都注册了哪些ChannelHandler然后就知道RocketMQ是如何处理和发送请求的。查阅相关资料当在阅读源码的时候对某一块代码功能实现不太清楚的时候可以通过查阅相关资料来辅助阅读包括但不限于以下几种通道官网书籍Github文章视频坚持最后一点也是最核心的一点就是坚持。只有你长期坚持读源码不停地思考总结不断提升自身技术的广度和深度找到适合自己的阅读方式阅读源码才会是越来越容易的一件事。

更多文章