灵魂小游戏
- 作为程序员,阅读源码是必备的技能包之一,只会调用api的程序员,不是一个好的程序员。阅读源码不仅可以让我们对所使用的框架、二方包得心应手,也便于问题的快速定位以及项目的快速落地,一个问题卡一天,相信不少人都遇到过,而读源码、搞清楚原理,看懂别人的代码是怎么写的,对于我们自己的编码能力、设计能力、架构能力都有极大的提升。
- 有人会说,读源码太枯燥了,没有啥意思;这里我突然想到一个问题,你读源码的时候你在想什么,这里借用村上春树在当我谈跑步时,我谈些什么里面提到的关于跑步的一句话,Pain is inevitable.Suffering is optional.(痛楚难以避免,而磨难可以选择)。而我觉得阅读源码也是如此,与其遇到问题各种csdn、stackoverflow、google,还不如先花点时间、花点心思,读读源码。
ChatGpt说这么读
大神说这么读
詹姆斯·高斯林(James Gosling)
马丁.福勒(Martin Fowler)
我的理解
我的套路
读哪些
我的理解,阅读源码分为4个步骤,第一步是读哪些:目的在于选定阅读范围,这里我们可以先以java语言的基础包为根基,例如常见的io、lang、collection、concurrent、nio等等,搞清楚常见的类加载器的委托机制、基础类型与包装类型的实现差异、反射的字段/方法/构造器该如何访问、并发相关的类有哪些(下面有一张,我很多年前画的)、输入输出流有哪些(字节流、字符流的差异、两者桥接的类是哪些,下面也有一张,我很多年前画的)。基本上,每看一个主题的时候,都尽量画出对应的类图,对一个主题有个大致的体感。
当你对java本身的机制有了更多了解后,常见的开源组织,例如apache、spring、netflix等等,开源聚居地github、gitee都可以多去逛逛,例如你想在github上查找apache所有开源的项目,就可以在github上随便搜索一个apache的项目,然后跳转到apache组织维度,从这个界面按照语言类型、按照点赞数目去排序,这样你就能知道apache在github上开源的java项目中,哪个项目最受欢迎了,目前看是dubbo。
看文章
当选定某个主题后,获取这个主题最快的方式,就是看官方文档,但是很多时候,文档都是英文的,对于像我这样英文没那么好的,就有一定障碍了。这个时候,直接在博客园或者csdn找到关于这个主题的文章,先看完再说,看看大家对于这个主题的看法,虽然有好有坏,但是你获取的内容还是很全面的。通过这些文章的描述以及官网的文档,我们大致能得出这个主题的适用场景有哪些、优缺点有哪些、应该配合其他什么组件联合使用、大致的设计思路以及原理是什么,有一个全局的轮廓就好。
写单测
单测的好处就毋庸置疑了,模块化、快速反馈、DDD驱动设计,一个一个小的测试用例能够帮助我们快速的了解这个主题的各个方面、run主线功能,同时对于一些细节的问题,也可以一个一个去仔细研究。例如再看netty代码的时候,netty支持http协议、tcp协议、各自的服务端代码该如何写、客户端代码该如何写、管道机制中如何添加自定义的加解码器、tcp的粘包/拆包机制是如何实现的、netty是如何利用nio的特性的、常用的编解码器有哪些,可能都是我们需要去了解、需要去单测验证的。
出总结
最好的学习方式就是输出,当我们逼迫做出类比,找到这个主题的架构设计跟其他类似的相比,有啥优缺点;针对一些细节的地方,能否找到你认为设计比较精巧的点,例如实现上比较精巧的点;当在画整体框架图、追细节的时候,不懂的地方,这个时候,再去追第三步的单测,详细再去读源码,解除心中的疑惑。
套路明细
我的实战
关于网络--Netty
看文章
- https://www.infoq.cn/article/netty-threading-modelNetty 系列之 Netty 线程模型
- https://github.com/netty/nettyNetty project
- https://blog.csdn.net/undergrowth/article/details/88888375?spm=1001.2014.3001.5501netty 4.1.34 源码浅析1
- https://blog.csdn.net/undergrowth/article/details/46363827java.nio基础篇之Buffer
- https://blog.csdn.net/undergrowth/article/details/46419473java_nio基础篇之Channel、Selector、Path、Paths、Pipe、Files
优缺点
- api使用简单,二次开发门槛低
- 功能强大,多种预置编解码器
- 定制能力强,通过ChannelHandler进行二次扩展
- 成熟、稳定、性能高、社区活跃
- 零拷贝(堆外直接内存进行Socket读写)
写单测
抓细节--源码阅读问题
- ChannelHandlerContext是如何传递共享参数的?
上下文数据通过委托给Channel携带(channel().attr(key)),因为Channel和ChannelHandlerContext都继承了AttributeMap,DelegatingChannelHandlerContext中采用委托方式
- 在解析http协议时,为什么会有HttpRequest/HttpContent/LastHttpContent这几个对象产生?
主要是通过HttpObjectDecoder(解析ByteBuf为HttpMessage和HttpContent或者LastHttpContent,取决于内容数据块的长度;如果没有内容,则是createMessage创建HttpMessage的子类;如果有固定长度内容,且小于maxChunkSize,则DefaultLastHttpContent;最后有很多内容的时候,就是DefaultHttpContent、DefaultLastHttpContent)----所以这里有HttpRequest和HttpContent内容需要业务端分开处理
同时在上面HttpObjectDecoder解析层的父类,有更进一步的解析,io.netty.handler.codec.ByteToMessageDecoder#fireChannelRead----将解析出来的列表元素循环迭代对后续的处理器,对于理解http的HttpRequest与HttpContent/LastHttpContent至关重要
出总结
- https://github.com/netty/netty/tree/netty-4.1.34.Final/example/src/main/java/io/netty/example官方源码例子
- https://github.com/undergrowthlinear/netty很早之前,自己总结相关测试代码
写在结尾
对于还没有完整读过源码的小伙伴,本文建议的源码阅读方式,不妨尝试下。例如我们说到mybatis,知道mybatis是Orm的实现,搞清楚o指的是java对象,r指的是关系型数据库,m映射是把Java方法和sql语句关联起来,这一核心观点后,再去看源码,会得心应手。
对于阅读源码,我想起来7年前的我,我当时有段时间,像中毒了一样,每个周看个框架源码,然后写一篇csdn,持续了大概一年左右。当你形成习惯后,例如我们说网络协议相关的框架,netty 、okhttp、httpclient等等之类的,你应该不陌生;提到字节码修改的,例如asm、cglib、javaasist 、byte-buddy,这些你应该不陌生;从你准备开始阅读源码,你会发现,要做的事情太多了,不过一步一个脚印,你会发现,付出是值得的。
作者 | 高止
来源 | 阿里云开发者公众号