大家好,我是三友~~本篇文章,我要说说如何阅读开源项目的源码。在说如何阅读源码之前,先简单说一下为什么要阅读源码。大致可以分为以下几个原因:最直接的原因是面试的需要。面试喜欢问源码。看完源码就可以跟面试官聊聊了。battle提高编程水平,学习编程思想和代码技巧,熟悉技术实现细节,提升设计能力……那么如何阅读源码呢?在这里我总结了18个心法,助你修炼神功,学好JDK。作为一个Javaer,不管是想看开源项目的源码,还是要学好JDK相关的技术。所有的Java开源项目,本质上都是利用JDK已有的类库和关键字来实现一个业务功能,所以学习JDK相关的类库就是看其他的源码基础。如果你不了解JDK,你会发现在阅读源码的时候有太多不明白的地方,会影响阅读源码的心情和信心。学习JDK主要包括用法和原理两部分。内容大致包括以下几个部分:集合相关,比如常见的Map、List、Queue的实现,包括线程安全和不安全并发相关,比如synchronized、volatile、CAS、AQS、锁、线程池、原子类等.io相关,包括bio和nio等反射相关网络编程相关...了解设计模式在一个优秀的开源项目中,设计模式无处不在,所以在开始阅读之前最好了解一些常见的设计模式源代码。当你了解了一些设计模式并在源码中遇到相关的设计模式后,你可以快速了解代码结构的设计,从而从整体的角度去阅读相关的代码。同时,学习设计模式不仅可以帮助我们阅读源代码,还可以帮助我们在日常开发中设计出更容易扩展的程序。如果你想学习设计模式,你可以看看《大话设计模式》这本书。如果不想看书,也可以找一些视频或者专栏。之前也写过一篇开源项目常用的设计模式的文章。2万字算那些玩烂的设计模式。感兴趣的朋友可以看看。从官方网站开始。官网是介绍开源项目的地方,也是了解一个开源项目的第一站。通过官网,我们可以快速了解项目,例如:项目的定位,一些核心概念、功能,以及教程的整体架构和设计。常见问题及解答...RokcetMQ官网在你了解项目的一些概念、功能等信息后,如果你阅读源码,发现代码是实现这些概念或功能的足迹,可以帮助你理解代码更好。熟悉源码模块结构对项目有一个大概的了解后,可以从Github上clone代码。官网有项目源码的Github地址。代码拉取成功后,可以简单分析项目源码模块,熟悉模块结构,分析模块功能,熟悉。以上是RocketMQ的源码。如果看过官网相关的一些概念介绍,就可以大致知道这些模块有哪些功能。RocketMQ的概念介绍比如源码中的broker模块,官网说broker主要负责消息的存储,所以broker模块的代码块必须主要实现消息存储的功能。还有一些模块可以根据单词的意思来判断,比如common模块,乍一看是存放一些常用类的模块,example模块是RocketMQ使用代码示例的模块等.跟着demo开始阅读。有的朋友在阅读源码的时候不知从何入手,最后从源码中的某个模块开始阅读。读完之后,越来越难读了。阅读源码的正确姿势应该是从demo开始阅读。比如我现在想看一下RocketMQ生产者是怎么发送消息的,整个过程是怎样的,那么首先至少要写一个发送消息的demo,看看代码是怎么写的。demo一般可以在官网查看。除了官网,开源项目一般在源码中都有对应的demo。代码放在示例模块中,比如上面提到的RocketMQ示例模块。最后,你可以通过谷歌搜索演示。DefaultMQProducerproducer=newDefaultMQProducer("sanyouProducer");//指定NameServer的地址producer.setNamesrvAddr("localhost:9876");//启动producerproducer.start();//省略代码。.Messagemsg=newMessage("sanyouTopic","TagA","Sanyou'sjavadiary".getBytes(RemotingHelper.DEFAULT_CHARSET));//发送一条消息,获取消息的发送结果,然后打印SendResultsendResult=producer.send(消息);以上是RocketMQ生产者发送消息的demo。消息发送的源码阅读从这段代码开始,一步步进入源码。这是阅读源码的开始。有目的的阅读有目的的阅读其实很容易理解。以上面生产者发送消息流程源码为例。阅读源码的第一个目的其实就是了解生产者发送消息的过程。除了弄清楚生产者发送的内容之外,您还可以出于其他目的阅读它。比如消息发送的核心逻辑是通过send方法实现的,那么除了消息发送之外,是不是可以理解producer在启动过程中做了什么,也就是start方法的作用。再比如生产者发送的消息必然涉及到网络通信相关的内容,那么了解RocketMQ的底层网络通信模型也可以算是一个目的。当你心中有了这些目标,你阅读源码的目的性就会很强,看完之后你会印象深刻。当然,如果你一开始想不到这些用途,也没关系。你可以先读下去,在读的过程中试着发现一些其他的目的。先抓主线,再抓支线。有的朋友在看源码的时候,每一个方法都想点,直到不知道代码进入了哪里。这实际上是非常不可取的。正确的做法应该是先掌握主线流程,再粗略看一下支线流程,就知道它要干什么了。看完主线再回去仔细阅读分支代码。比如在Spring中,ApplicationContext在使用之前需要调用refresh方法,refresh方法定义了刷新整个容器的执行过程代码。refresh方法截图阅读这段代码时,可以先大致看一下refresh中的各个方法是干什么的。看完之后可以详细阅读每段代码的具体实现,比如prepareRefresh是干什么的,obtainFreshBeanFactory如何获取BeanFactory,prepareBeanFactory对BeanFactory做了什么等等。不要深入研究实现细节。有的朋友看书的时候喜欢钻研。弄清楚每一行代码是如何实现的,不仅非常困难,而且也是不可取的。比如我们都知道在SpringBean的生命周期中,当有一种基于xml声明bean的方式时,Spring就会解析xml生成BeanDefinition。当你想了解Bean的生命周期过程时,其实没有必要去深究Spring是如何解析xml生成BeanDefinition的。这对你从整体上理解Bean的生命周期意义不大。只知道它最终会被转换成一个BeanDefinition。什么时候扣除实施细则?当你需要使用它的时候,比如你遇到了一个bug或者你需要扩展以防止你理解功能实现。在大胆猜测源码的同时,我们也需要发挥一点想象力来猜测功能是如何实现的。猜测不是盲目猜测,而是根据目前所了解的一些知识、技术或思想进行的合理猜测。比如当你已经知道OpenFeign最终会为每个FeignClient接口生成动态代理对象,而后面注入的对象都是代理对象。在代理对象中实现了RPC请求之后,那么大家在学习dubbo的时候,是不是可以猜到注入的dubbo接口最终是一个动态的代理对象,而这个代理对象也实现了RPC请求呢?之后大家在阅读代码的时候需要注意是否有动态代理生成的代码。这是一个目的。一旦找到动态代理相关的代码,那么这段代码很可能就是dubboRPC实现的核心。学会看班名,不要小看班名。优秀的代码命名从名字上就耳熟能详,所以从类名上也能窥见该类的一些蛛丝马迹。下面列出几种常用的以Registry结尾的命名习惯,一般都是存储功能。比如Spring中的SingletonBeanRegistry就是用来保存单例bean的;Mybatis中的MapperRegistry用于保存Mapper接口。Support、Helper、S、Util(s)一般都是以Filter结尾的工具类,Interceptor一般是拦截,一般和责任链模式(Chain)结合使用。那些以Event和Listener结尾的,一般都是基于观察者模式的Event发布订阅模型来实现的。。。除了一些常见的命名约定外,还有一些项目特有的命名约定。比如Spring中常见的以PostProcessor结尾的都是扩展接口。实现这些接口,就可以得到一个比较核心的组件,从而实现Spring的扩展。其实很多开源项目的命名都比较偏向于Spring的命名风格。遇到类似Spring命名的东西,可以大胆猜猜类的作用。学会看班级结构也很重要。也可以帮助我们看到类的大致功能。ApplicationContext,如上图,是Spring中ApplicationContext的继承体系。当你需要了解ApplicationContext时,可以先熟悉一下它的父接口的作用。当你大致了解了各个接口的作用,那么ApplicationContext有什么作用呢?差不多清楚了。除了看类继承体系,还可以浏览类提供的通用方法,了解对外提供的功能。类方法可以通过快捷键ctrl+F12(mac:fn+command+F12)查看,还支持模糊查找方法名。我个人比较喜欢这个快捷键ApplicationContext来概括类的职责。当我们看完一个类的代码后,一定要总结这个类的职责,理解这个类的含义。一般情况下,一个类只有一个核心职责,遵循单一职责的设计原则。比如RocketMQ中就有一个类MQClientAPIImplMQClientAPIImpl。其实从名字上看不出这个类的主要功能是什么,但是在阅读代码的时候发现每个方法最终都会调用RemotingClient方法,而RemotingClient只有一个NettyRemotingClient的实现。所以从这个实现和类名可以猜到RemotingClient就是发送网络请求的客户端,所以看了MQClientAPIImpl的源码才知道MQClientAPIImpl类的职责大致就是封装参数,然后发送消息给MQ通过RemotingClient。当你知道了这个类的职责,那么当其他地方调用这个类的方法时,你就知道自己在干什么了。习惯阅读注释当你在阅读源代码时,如果有注释,最好先阅读注释,这样可以帮助你弄清楚类或方法的功能。先了解功能再看源码就容易多了。评论一般是英文的。不懂的可以装个插件写好评论和功能。注释不需要对每一行代码都进行注释,当然,如果你愿意,问题不大,但是注释应该包括以下几点:核心类和方法实现的核心功能核心类和方法实现的代码实现细节DefaultMessageStore中不好理解的如图,这是我看的关于RocketMQ中DefaultMessageStore类的评论。该类是RocketMQ中非常核心的一个类。从名字就可以看出它与消息的存储有关。这个类的功能很多,所以我写了很多注释,列出了这个类的主要功能和这些功能的一些实现细节。及时总结思路输出当你看完某个功能模块后,可以尝试总结一下这个功能的逻辑或思路。比如当你理解了CAS的思想后,你会发现线程安全不仅可以通过锁来保证,还可以基于乐观锁来保证。总结之后可以输出成文档,或者流程图。我个人喜欢画画。这里推荐两个在线绘图工具:我经常用的processondraw.ioprocesson,功能很多,但是要收费;draw.io是免费的,图标和颜色比processon好看。平时文章里的贴图都是用draw.io画的。这里多说一句,总结思路还是很重要的。看了很多源码后,发现很多技术或功能的实现原理,最终都是殊途同归。提前了解相关技术。一般来说,并不是一个开源项目的所有技术都是自己实现的。它还取决于其他一些框架或想法。提前了解这些框架或思路,可以帮助你更好地阅读和理清代码。比如RocketMQ底层是基于Netty框架实现网络通信的。当你了解了Netty,知道了Netty在启动的时候需要注册一堆ChannelHandlers来处理网络请求,那么在阅读RocketMQ的底层网络通信函数的时候,你会去Netty启动的代码看看是哪个ChannelHandlers注册了,你就知道RocketMQ是如何处理和发送请求的了。查阅相关资料阅读源码时,如果对某段代码的功能不是很清楚,可以查阅相关资料辅助阅读,包括但不限于以下渠道:官网书籍Github文章和视频坚持最后一点也是最核心的一点就是坚持。只有长期坚持阅读源码,不断思考,不断总结,不断提升自身技术的广度和深度,找到适合自己的阅读方式,阅读源码才会越来越轻松。
