如何学会阅读源码?

作者 | youzhibing

链接 | cnblogs.com/youzhibing/p/9553752.html

1.读源码的经历

刚参加工作那会,没想过去读源码,更没想过去改框架的源码;总想着别人的框架应该是完美的、万能的,应该不需要改;另外即使我改了源码,怎么样让我的改动生效了?项目中引用的不还是没改的jar包吗。回想起来觉得那时候的想法确实挺……

工作了一年多之后准备跳槽了,开始了一轮的面试,其中有几个面试官就问到了相关的源码问题:ArrayList、HashMap的底层实现,spring、mybatis的相关源码。问源码的面试一般就是回去等消息,然后就没然后了。

那时候开始意识到,源码这东西在之前的工作的中感受不到,但是在面试中好像面的还挺频繁的,从此有意识的开始了jdk部分源码的阅读(主要是集合)。一开始看源码,看的特别糙,知道个大概,知道ArrayList的底层实现是数组,HashMap的底层是散列表(数组+链表);更深入一点的扩容、hash碰撞等等就不知道了。

读spring源码起于工作中遇到了一个问题(spring jdbcTemplate事务,各种诡异,包你醍醐灌顶!),排查一段时间最终是解决了,但过程让我非常难受,各种上网查资料、各种尝试,感觉就像大海捞针一样,遥遥无期。我下定决心,我要看一看spring的源码,于是我买了一本《spring源码深度解析》,结合着这本书、打开着eclipse,开始了spring的源码阅读之旅。至此,读源码成了习惯,源码已经进入了我的心里。

后来,springboot的火热,让我也想蹭上一蹭,于是有了springboot的启动源码系列,虽然还在进行中,但是我相信我能将其完成;工作中用到了shiro,我又结合着《跟我学shiro》将shiro的源码看了个大概,有了shiro源码系列博文,还差一篇认证与授权(应该很快就能面世),shiro源码系列就封笔了。最近在搭建自己的后台管理系统,用到了quartz,集成的过程也遇到了一些问题,因此有了quartz的两篇文章。

慢慢的,从一味的网上找资料变成了很多时候会从源码中找答案。不求能读太多的源码,但愿自己接触的技术都能读上一读,路漫漫其修远兮,吾将上下而求索!

2.我为什么读源码

很多人一定和我一样的感受:源码在工作中有用吗?用处大吗?很长一段时间内我也有这样的疑问,认为哪些有事没事扯源码的人就是在装,只是为了提高他们的逼格而已。

那为什么我还要读源码呢?一刚开始为了面试,后来为了解决工作中的问题,再后来就是个人喜好了。说的好听点是有匠人精神;说的委婉点是好奇(底层是怎么实现的);说的不自信点是对黑盒的东西我用的没底,怕用错;说的简单直白点是提升自我价值,为了更高的薪资待遇(这里对真正的技术迷说声抱歉)。

源码中我们可以学到很多东西,学习别人高效的代码书写、学习别人对设计模式的熟练使用、学习别人对整个架构的布局,等等。如果你还能找出其中的不足,那么恭喜你,你要飞升了!会使用固然重要,但知道为什么这么使用同样重要。从模仿中学习,从模仿中创新。

读源码不像围城(外面的人想进来,里面的人想出去),它是外面的人不想进来,里面的人不想出去;当我们跨进城内,你会发现(还是城外好,皮!)城内风光无限,源码的海洋任我们遨游!

3.我是怎么样读源码的

内容了解

首先我们要对我们的目标有所了解,知道她有什么特点,有些什么功能。对对方都还不了解,就想着进入别人的内心世界,那不是臭不要脸嘛,我们要做一个有着流氓心的绅士;对她有个大致的了解了,就可以发起攻势,一举拿下。

那么怎么样了解了,方式有很多,我这里提供几种,仅供参考

最好的方式就是官方参考指南,亲生父母往往对孩子是最了解的,对孩子的描述也是最详细的;比如Spring Boot Reference Guide就是对springboot最详细的描述,怎么样使用springboot、springboot特性等等,通过此指南,springboot在你面前一览无遗;

但是,springboot毕竟是外国人的孩子,如果英语不好,估计读起来有点头疼了,不过我们有google翻译呀,咬咬牙也是能看的。源码世界的丈母娘、老岳丈是非常慷慨的!

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

再次就是博客,虽然可能觉得知识点比较散,但是针对某个知识点却特别的细,对彻底掌握非常有帮助,园子内就有很多技术大牛,写的博客自然也是非常棒,非常具有学习价值。当然还有社区、论坛、github、码云等等。这就是源码的朋友圈,我们从中也能获取到非常多关于源码的信息。

设计模式的了解

优秀的框架、技术从不乏设计模式;jdk源码中就应用了很多设计模式,比如IO流中的适配器模式与装饰模式、GUI的观察者模式、集合中的迭代器模式等等;spring源码中也是用到了大量的设计模式。设计模式有什么优点、各适用于什么场景,不是本文的内容,需要我们大家自行去了解。

我们只需要对一些常用的设计模式有个大致了解,再去读源码是比较好的;不需要将23种设计模式都通读,也不需要将常用设计模式完全理解透;对于全部通读,我们时间有限,另外有些模式确实不太好理解、用的少,性价比不高,没必要全部都读。

推荐书籍:《Head First Design Patterns》(中文版:《Head First 设计模式》)、《Java与模式》;

另外我比较推荐的一种学习设计模式的方式是读别人博客:java_my_life,刘伟技术博客,chenssy的设计模式;

设计模式之于源码,就好比逛街购物之于女人,想顺利勾搭源码,我们需要好好掌握设计模式这个套路。

配合ide进行断点追踪

我们通过源码的圈子对源码的了解终究只是停在表面,终究还是没有走进她的内心,接下来我就和大家分享下,我是如何走进她的内心的!

相信看过我的源码博客的小伙伴都知道,我非常喜欢通过idea断点来进行源码追踪,断点追踪源码是我非常推荐的一种方式。断点不仅可以用来调试我们的代码,也可以用来调试我们用到的框架源码。

面对未知的、茫茫多的源码,我们往往没有足够的时间、经历和耐心去通读所有源码,我们只需要去读我们关注的部分即可(有人可能会说我都不关心,这…)。那为什么要用断掉调试的方式来跟源码,而不是直接从源代码入手去跟我们关注的部分呢?

尝试过的小伙伴应该知道,如果我们对源码不熟悉,直接通过源码的方式去跟,一方面很容易迷路(多态,会有很多子类实现),不知道接下来跟哪一个,另一方面也很容易跟丢,当我们跟入的很深的时候,很有可能就忘记上一步跟到哪了。

下面我会举例来说明我是如何进行断点追踪的,以spring-boot-2.0.3之quartz集成,不是你想的那样哦!和 spring-boot-2.0.3之quartz集成,数据源问题,源码探究 为背景来讲,需要搞清楚两个点:springboot是如何向quartz注入数据源的,quartz是如何操作数据库的

springboot向quartz注入数据源

QuartzAutoConfiguration是springboot自动配置quartz的入口

将quartz的配置属性设置给SchedulerFactoryBean;将数据源设置给SchedulerFactoryBean:如果有@QuartzDataSource修饰的数据源,则将@QuartzDataSource修饰的数据源设置给SchedulerFactoryBean,否则将应用的数据源(druid数据源)设置给SchedulerFactoryBean,显然我们的应用中没有@QuartzDataSource修饰的数据源,那么SchedulerFactoryBean中的数据源就是应用的数据源;将事务管理器设置给SchedulerFactoryBean。SchedulerFactoryBean,负责创建和配置quartz Scheduler,并将其注册到spring容器中。SchedulerFactoryBean实现InitializingBean的afterPropertiesSet方法,里面有可以设置数据源的过程


可以看到通过org.quartz.jobStore.dataSource设置的dsName(值为quartzDs)最后会被替换成springTxDataSource.加scheduler实例名(我们的应用中是:springTxDataSource.quartzScheduler)。springboot会注册两个ConnectionProvider给quartz:一个dsName叫springTxDataSource.quartzScheduler,有事务;一个dsName叫springNonTxDataSource.quartzScheduler,没事务。

quartz如何操作数据库

我们通过停止定时任务来跟下quartz对数据库的操作


发现quartz用如下方式获取connection

conn = DBConnectionManager.getInstance().getConnection(getDataSource());

那么我们的job中就可以按如下方式操作数据库了

public class FetchDataJob extends QuartzJobBean {// private String dataSourceName = "quartzDs"; // 用此会找不到// private String dataSourceName = "springNonTxDataSource.quartzScheduler"; // 不支持事务// private String dataSourceName = "springTxDataSource.quartzScheduler"; // 支持事务private final String insertSql = "INSERT INTO tbl_sys_user(name, age) VALUES(?,?) ";private String schedulerInstanceName = "quartzScheduler"; // 可通过jobDataMap注入进来@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {String dsName = LocalDataSourceJobStore.NON_TX_DATA_SOURCE_PREFIX+ schedulerInstanceName; // 不支持事务//String dsName = LocalDataSourceJobStore.TX_DATA_SOURCE_PREFIX + schedulerInstanceName; // 支持事务try {Connection connection = DBConnectionManager.getInstance().getConnection(dsName);PreparedStatement ps = connection.prepareStatement(insertSql);ps.setString(1, "张三");ps.setInt(2, 25);ps.executeUpdate();ps.close();connection.close(); // 将连接归还给连接池System.out.println("插入成功");} catch (SQLException e) {e.printStackTrace();}}public void setSchedulerInstanceName(String schedulerInstanceName) {this.schedulerInstanceName = schedulerInstanceName;}
}

明确我们的目的,找到合适的切入点,进入断点调试追踪也就容易了。

任我说的天花乱坠,你仍无动于衷,那也只是我一厢情愿,只有局中人才能体会到其中的奥妙!

4.总结与感悟

从上至下全部通读的方式,个人不太推荐,这是建立在很熟悉的基础上的,当我们对某个框架已经比较熟悉了,再从上至下进行通读,彻底了解,这是我认为正确的方式;但是从不熟悉到熟悉这个过程,个人不推荐全部通读,而是推荐上面我推荐的方式 - 断点局部追踪。

很多时候,我们的博文都只是授之以鱼,而我们也只是从中得到鱼;而这篇的目的则是授之以渔,我希望大家从中学到捕鱼的方法,而不是一味的等待别人的鱼;希望大家能够自给自足,也能把鱼和渔都授予其他人。

只要我们开始去读源码,慢慢的就会形成自己的一套读源码的方式;每个人的方式都不一样,合适自己的才是最好的。行动起来,用合适的方式去俘获你的的她吧!

纯属个人之拙见,欢迎讨论和斧正!


【END】

关注下方二维码,订阅更多精彩内容

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

求模和求余

一直以为求模和求余是一回事,发现这两者是不同的。以下为网上转载的资料: 通常情况下取模运算(mod)和求余(rem)运算被混为一谈,因为在大多数的编程语言里,都用%符号表示取模或者求余运算。在这里要提醒大家要十分注意当前环境下%运…

利用Dockefile将Python的py文件项目代码打包为Docker镜像

1.创建python项目 【备注:一定要将项目python环境依赖存至本项目下,默认依赖本机python环境(会造成依赖包过多)】 2.创建main.py文件,完成程序代码 主要功能就是获取"https://www.hao123.com/"网址页面源代码,并存储…

面试官:如何实现幂等性校验?

作者 | wangzaiplus来源 | https://www.jianshu.com/p/6189275403ed一、概念幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次比如:订单接口, 不能多次创建订单支付接口, 重复支付同一笔订单只能扣一次钱支付宝回调接口, 可能会多次回调, 必须处理…

阿里为什么禁用Executors创建线程池?

作者 | 何甜甜在吗来源 | http://rrd.me/eUh6V看阿里巴巴开发手册并发编程这块有一条:线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,通过源码分析禁用的原因。写在前面首先感谢大家在盖楼的间隙阅读本篇文章&#xff0…

Debian11镜像更新为阿里巴巴开源镜像站镜像,切换root用户,解决用户名不在sudoers文件中此事将被报告,Debian11 文件夹对话框、火狐浏览器、命令终端等没有最大化和最小化

选择Debian作为编程开发最佳Linux的理由: Debian是面向程序员的最古老,最出色的Linux发行版之一。Debian提供了具有.deb软件包管理兼容性的超稳定发行版。Debian为程序员提供了许多最新功能。因此,它具有一个特殊的编程空间。Debian是开发人员…

SCCM2012R2部署之四:配置客户端发现

前面3个章节我们简单的,介绍了安装配置和相关的组件。接下来我们需要给大家介绍的是如何配置客户端发现,让SCCM能真正管控到AD中的所有终端,来提供IT运维的效率。首先我们打开SCCM控制台,如图4-1,这就是我们安装完SCCM…

Debian11安装VLC Media Player视频播放器

在终端内执行下面命令: sudo apt install vlc

面试官:使用SpringBoot如何开发邮件发送系统?

作者 | yizhiwazi来源 | www.jianshu.com/p/5eb000544dd7SpringBoot 开发邮件发送系统还是比较方便的,在开始之前我们先来了解一下和发送邮件有关的基础知识。基础知识什么是SMTP?SMTP全称为Simple Mail Transfer Protocol(简单邮件传输协议&…

Python计算校验文件的MD5、SHA1、SHA256和CRC32

# -*- coding: utf-8 -*- import os from hashlib import md5, sha1, sha256 from zlib import crc32strFilePath os.path.join(os.getcwd() "\\" "“捷创源科技”公众号.jpg")def getMd5(strFilePath): # 计算md5mdfive md5()with open(strFilePath,…

拼多多面试|如何用 Redis 统计独立用户访问量?

作者 | 沙茶敏碎碎念来源 | www.cnblogs.com/xiaoMzjm/p/5223799.html众所周至,拼多多的待遇也是高的可怕,在挖人方面也是不遗余力,对于一些工作3年的开发,稍微优秀一点的,都给到30K的Offer当然,拼多多加班…

被一个熟悉的面试题问懵了:StringBuilder 为什么线程不安全?

作者 | 千山qianshan 来源 | juejin.im/post/5d6228046fb9a06add4e37fe前言周五去面试又被面试的一个问题问哑巴了面试官:StringBuilder和StringBuffer的区别在哪? 我:StringBuilder不是线程安全的,StringBuffer是线程安全的 面试…

面试官:HTTPS 为什么是安全的?说一下他的底层实现原理?

作者 | leapmie来源 | urlify.cn/zQj6f2这篇干货不错,把HTTPS的原理讲清楚了,而且容易懂,建议大家好好读一下。# HTTPS随着 HTTPS 建站的成本下降,现在大部分的网站都已经开始用上 HTTPS 协议。大家都知道 HTTPS 比 HTTP 安全&…

PyQt5在对话框中打开外部链接的方法

利用PyQt5部分控件的Link属性链接 PyQt5有几个控件带有 setOpenExternalLinks , 如 QLabel、QTextLabel 、 QTextBrowser 等 当 setOpenExternalLinks 值为TURE 表示可通过html 添加 A 标签打开外部链接, 如设置: 我测试的是 QLabel 标签控件 self.lab…

面试官:为什么 Spring 中的 bean 默认为单例?

作者 | 小小木来源 | http://1t.click/ksQ熟悉Spring开发的朋友都知道Spring提供了5种scope分别是singleton、prototype、request、session、global session。如下图是官方文档上的截图,感兴趣的朋友可以进去看看这五种分别有什么不同。今天要介绍的是这五种中的前两…

博主推荐【文件Hash校验工具V1.0 -免费版】

文件Hash校验工具有什么用途? ​Hash校验工具可以用来计算文件的MD5、SHA1、SHA256、CRC32值。简单来说,MD5值就是文件的身份ID,并且具有唯一性。通过比对MD5值,用户能够检查文件是否被篡改过,确保安全性。一般来说&a…

基于深度学习的瓷砖色差分类方法研究——学习笔记(评价:色差的定义太模糊。。。问题描述不清楚,太水了)

文章目录 摘要0 引言1 瓷砖图像处理1.1 图像采集1.2 图像处理 2 基于深度学习的瓷砖色差分类算法设计2.1 数据预处理2.2 卷积神经网络的设计2.3 实验设计 3 瓷砖色差分类平台的设计与实现 摘要 瓷砖是人类建筑不可或缺的一种材料,而瓷砖品质最重要的指标之一就是色…

面试官 | 讲一下如何给高并发系统做限流?

作者 | nick hao来源 | uee.me/cDuRD在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。本文结合作者的一些经验介绍限流的相关概念、算法和常规的实现方式。缓存缓存比较好理解,在大型高并发系统中,如果没有缓存数据库将分分钟被…

Python利用multiprocessing实现多进程,Pyinstaller打包python多进程程序出现多个窗口

一、为什么需要采用multiprocessing多线程技术 自己在做文件Hash校验工具V1.0小工具软件时,需要读取文件,计算文件的MD5、SHA1、SHA256和CRC32这些Hash值,对于小文件能够很快计算出hash值,但是对于大文件需要花费一些时间,不知道进度如何?使用进度条指示也无法正确显示进…

面试官 | 说一下数据库如何分库分表?

作者 | butterfly100来源 | cnblogs.com/butterfly100/p/9034281.html一. 数据切分关系型数据库本身比较容易成为系统瓶颈,单机存储容量、连接数、处理能力都有限。当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从库、优化索…

面试官 | JVM 为什么使用元空间替换了永久代?

7:40到11:40历时4个小时完成了该文,看到电脑中左边的便签了么,我也是拼了。在Java8和以后版本中JVM的内存结构慢慢发生了变化。作为面试官如果你还不知道,那么面试过程中是不是有些露怯?作为面试者,如果知晓这些变化&a…