如何去阅读源码,我总结了18条心法

那么到底该如何去阅读源码呢?这里我总结了18条心法,助你修炼神功

图片

学好JDK

身为一个Javaer,不论要不要阅读开源项目源码,都要学好JDK相关的技术。

所有的Java类开源项目,本质上其实就是利用JDK已有的类库和关键字实现一种业务功能,所以学会了JDK相关的类库是看其它的源码基础。

如果你不懂JDK,你去阅读源码会发现有太多看不懂的地方,会影响读源码的心情和信心。

学习JDK主要包括使用和原理两部分。内容大致包括以下几部分:

  • 集合相关,比如常见的Map,List,Queue的实现,包括线程安全与不安全

  • 并发相关,比如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)结尾的一般都是工具类

  • 以Filter,Interceptor结尾的一般都是拦截作用,一般会配合责任链模式(Chain)使用

  • 以Event、Listener结尾的一般都是基于观察者模式实现的事件发布订阅模型

  • ...

除了一些比较通用的命名习惯,也有一些项目独有的一些命名习惯。

比如说Spring中常见的以PostProcessor结尾的都是扩展接口,实现这些接口可以拿到某个比较核心的组件,从而实现对Spring的扩展。

其实很多开源项目的命名都比较偏向Spring的命名风格,当你遇到了跟Spring的命名比较像的时候,那么可以大胆猜测类的作用。

学会看类结构

类结构也非常重要,他也能够帮助我们窥探类的大致功能。

图片

ApplicationContext

如上图,是Spring中ApplicationContext的继承体系,当你需要了解ApplicationContext的时候,可以先去熟悉一下它的父接口的作用,当你大致弄明白了每个接口的作用,那么ApplicationContext有啥作用就大致就清楚了。

除了可以看类继承体系,还可以浏览一下类大致提供了哪些方法,了解对外提供的功能。

类方法通过快捷键 ctrl+F12(mac:fn+command+F12)查看,并且还支持模糊搜索方法名,我本人就非常喜欢这个快捷键

图片

ApplicationContext

总结类的职责

当我们在读完一个类的代码的时候,一定要总结这个类的职责,明白这个类存在的意义。一般情况下一个类核心职责只有一个,遵循单一职责的设计原则。

举个例子,在RocketMQ中有一个类MQClientAPIImpl

图片

MQClientAPIImpl

其实从名字大概看不出这个类主要是有什么功能,但是当我读代码的时候发现每个方法最终都调用RemotingClient方法,而RemotingClient只有一个实现NettyRemotingClient,所以从这个实现和类名可以猜出来RemotingClient是发送网络请求的客户端,所以当读完MQClientAPIImpl源码之后,我就知道了MQClientAPIImpl这个类的职责大致是封装参数,然后通过RemotingClient向MQ发送消息的。

当知道这个类的职责的时候,那么其它地方在调用这个类的方法的时候,就知道大概在做什么事了。

习惯阅读注释

当你在读源码的时候,如果有注释,最好能先读一下注释,这样能帮助你厘清类或者方法的功能,先知道功能,再去读源码就容易多了。

注释一般都是英文,如果看不懂,可以装个插件

图片

写好注释

俗话说的好记性不如烂笔头,写好注释也是阅读源码中很重要的一个环节,好的注释可以帮助快速回忆起实现细节和功能。

注释并不需要对每行代码都注释,当然如果你愿意也没多大问题,但是注释应包括以下几点内容:

  • 核心类和方法实现的核心功能

  • 核心功能大致的实现逻辑

  • 核心的成员变量的作用

  • 方法中不易读懂的代码实现细节

图片

DefaultMessageStore

如图,是我读RocketMQ中对于DefaultMessageStore类阅读的注释,这个类是RocketMQ中一个非常核心的类,从名字可以看出来跟消息的存储有关。这个类的功能非常多,所以我写了很多注释,列举了这个类主要有哪些功能和这些功能实现的一些细节。

总结思想,及时输出

当你读完某个功能模块的时候,就可以尝试对这块功能实现逻辑或者思想进行总结。

比如说,当你了解了CAS思想的时候,你会发现,原来保证线程安全不仅仅可以通过加锁的方式,还可以基于乐观锁的方式来实现。

在总结之后可以输出成一个文档,又或者是流程图。我个人比较喜欢画图,这里推荐两个在线画图工具:

  • processon

  • draw.io

processon我平时就在用,功能多,但是需要收费;draw.io的话免费,图标和颜色感觉比processon好看,平时文章中的贴图就是用draw.io画的。

这里多说一句,总结思想还是非常重要的,在我阅读了很多源码之后,我发现很多技术或者功能的实现原理最终都是殊途同归。

提前了解依赖的技术

一般一个开源项目不是所有的技术都是自己实现的,它也会依赖一些其它的框架或者是思想,提前了解这些框架或者是思想,可以帮助你更好地阅读和理清代码。

比如说,RocketMQ底层是基于Netty框架实现网络通信的,当你对Netty有所了解,知道Netty在启动的时候需要注册一堆ChannelHandler用来处理网络请求,那么在读RocketMQ底层网络通信功能的时候你就可以去找一下Netty启动的代码,看看都注册了哪些ChannelHandler,然后就知道RocketMQ是如何处理和发送请求的。

查阅相关资料

当在阅读源码的时候,对某一块代码功能实现不太清楚的时候,可以通过查阅相关资料来辅助阅读,包括但不限于以下几种通道:

  • 官网

  • 书籍

  • Github

  • 文章

  • 视频

坚持

最后一点也是最核心的一点就是坚持。只有你长期坚持读源码,不停地思考,总结,不断提升自身技术的广度和深度,找到适合自己的阅读方式,阅读源码才会是越来越容易的一件事。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/176037.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VUE excel表格导出

js代码 //下载模板 downloadExl() { // 标题 const tHeader [‘xxx’,xxx,xx名称,电枪xx,协议xx,snxx]; // key const filterVal [agentName, stationName, equName, channelNumber, manufacturer, sn, ]; // 值 const datas [ { agentName: 你好, stationName: 我们, e…

【实战教程】改进YOLOv5与Seg网络结合:实时车道线精准识别系统(含源码及部署步骤)

1.研究的背景 随着自动驾驶技术的不断发展,车道线的实时分割成为了自动驾驶系统中的重要任务之一。车道线的准确分割可以为自动驾驶系统提供重要的环境感知信息,帮助车辆进行准确的路径规划和决策。因此,开发一种高效准确的车道线实时分割系…

Threejs_11 补间动画的实现

啥是补间动画呢?其实就是我们在threejs中移动一个物体的时候,不让他是瞬时移动,让他跟css动画的transition一样,有个过度效果,就是补间动画。补间动画如何设置呢? 补间动画实现 1.引入补间动画库 在我们…

爬取春秋航空航班信息

一、使用fiddler爬取小程序春秋航空航班信息 使用Fiddler爬取春秋航空微信小程序(手机上由于网络问题,无法进入,使用电脑版) 搜索航班信息 搜索记录 使用Fiddler查找url(没有得到有效url) 继续查找,发现航班信息列…

前端实现表格生成序号001、002、003自增

我们最终想要实现的效果如图&#xff0c;从后端获取数据之后&#xff0c;不使用data中的id&#xff0c;而是使用自己生成的按照顺序自增的序号id。 script <template><el-table :data"sticker" border style"width: 100%" id"stickerList&q…

uniapp 富文本以及移动端富文本的展示问题

富文本展示有几种方式: 1.<view v-html"content"></view> 2. uniapp自带组件 rich-text rich-text | uni-app官网 <rich-text :nodes"content"></rich-text> 3.uView组件 u-parse Parse 富文本解析器 | uView 2.0 - 全面兼…

前端js调取摄像头并实现拍照功能

前言 最近接到的一个需求十分有意思&#xff0c;设计整体实现了前端仿 微信扫一扫 的功能。整理了一下思路&#xff0c;做一个分享。 tips: 如果想要实现完整扫一扫的功能&#xff0c;你需要掌握一些前置知识&#xff0c;这次我们先讲如何实现拍照并且保存的功能。 一. windo…

HSV映射到圆锥坐标系

def bgr2hsvcone(img):arr_hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV)h arr_hsv[..., 0] / 180. * 2s arr_hsv[..., 1] / 255.v arr_hsv[..., 2] / 255.x np.cos(h * np.pi) * s * vy np.sin(h * np.pi) * s * vreturn np.stack((x, y, v), axis-1)

Java-接口

接口 接口 接口就是公共的行为规范,只要实现时符合标准就可以通用. 接口可以看成是: 多个类的公共规范,是一种引用数据类型. 使用关键字interface实现接口. 接口是不能被实例化的. 接口中的成员变量默认是 public static final 接口中只能有抽象方法,当中的方法不写,也是pu…

bclinux aarch64 ceph 14.2.10 云主机 4节点 fio

ceph -s 由于是基于底层分布式存储的云主机&#xff0c;数据仅供参考 本地云盘性能 direct1 1M读取 IOPS134, BW134MiB/s [rootceph-client rbd]# cd / [rootceph-client /]# fio -filenamefio.bin -direct1 -iodepth 128 -thread -rwread -ioenginelibaio -bs1M -size10G -n…

倍福控制器搭建IgH环境

最近收到了倍福CX5230控制器&#xff0c;控制器上自带EBUS总线扩展的IO&#xff0c;使用的是CCAT网卡&#xff0c;在控制器上安装preempt-rt Linux系统&#xff0c;再安装IgH。 IgH正常识别到了扩展的IO模块。 运行控制程序&#xff0c;可以正常控制IO输出。

OpenAI再次与Altman谈判;ChatGPT Voice正式上线

11月22日&#xff0c;金融时报消息&#xff0c;OpenAI迫于超过700名员工联名信的压力&#xff0c;再次启动了与Sam Altman的谈判&#xff0c;希望他回归董事会。 在Sam确定加入微软后&#xff0c;OpenAI超700名员工签署了一封联名信&#xff0c;要求Sam和Greg Brockman&#x…

elasticsearch安装分词器插件

查看插件安装情况 elasticsearch-plugin list 插件在线安装 bin/elasticsearch-plugin install analysis-icu 离线安装ik分词 cd plugins wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.7/elasticsearch-analysis-ik-7.17.7.zip unzi…

云备份——初步认识及环境搭建

文章目录 整体功能简介云备份功能实现目标服务器程序负责功能细分服务端模块划分客户端功能细分客户端模块划分 环境搭建gcc安装 jsoncppbundle库 与 httplib库安装 整体功能简介 云备份功能 自动将本地计算机上指定文件夹中需要备份的文件上传备份到服务器中 并且能够通过浏…

漏洞复现--捷诚管理信息系统多处SQL注入

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

Vue2 基本语法

Vue2 基本语法 前言Vue2 基本语法脚手架文件结构关于不同版本的Vuevue.config.js配置文件ref属性props配置项mixin(混入)插件scoped样式总结TodoList案例webStorage组件的自定义事件全局事件总线&#xff08;GlobalEventBus&#xff09;消息订阅与发布&#xff08;pubsub&#…

Deepmind开发音频模型Lyria 用于生成高品质音乐;创建亚马逊新产品评论摘要

&#x1f989; AI新闻 &#x1f680; Deepmind开发音频模型Lyria 用于生成高品质音乐 摘要&#xff1a;Deepmind推出名为Lyria的音频模型&#xff0c;可生成带有乐器和人声的高品质音乐。Lyria模型针对音乐生成的挑战&#xff0c;解决了音乐信息密度高、音乐序列中的连续性维…

html网站-关于发展历程的案例

一、案例一 1.效果图&#xff1a; 2.代码&#xff1a; 所用到的文件自行在官网下载&#xff0c;也可在git上拉取。 <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /><meta…

VMware Workstation系列:Windows10 优化VMware虚拟机运行速度总结(单台、多台-ESXI)

Windows10 优化VMware虚拟机运行速度总结 一. 单台或两台同时运行前言&#xff1a;优化方法环境&#xff1a; 1、清除多余快照2、清理磁盘。3、虚拟机全局设置5、设置“优先级”6、设置“设备”7、编辑虚拟机设置8、分配合适的内存和CPU 二. 多台并行背景&#xff1a;一. 下载1…

Redis主从复制,哨兵和Cluster集群

主从复制&#xff1a; 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份&#xff08;和同步&#xff09;&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。 缺陷&#xff1a;故障恢复无法自动化…