技术精进的三境界

最近更新了一篇 Docker 的文章,朋友跟我反馈说效果并不是很好,我回头看了下,的确没有我自己的特色,没有太多思考,让公众号显得有些「百货」了。经过反思,今后只在个人博客更新 Docker 相关的个人学习经验(传送门),个人公众号还是主要推送和 Java 结合较为紧密的内容。

前言

前不久公众号后台有人给我留言,请教如何体系地学习 Java 知识,我当时心想:这话题太大,Java 技术栈也太深,不是一篇文章能说清楚的,但要说学习技巧,却的确是有规律所寻,秉持着「授人以鱼不如授人以渔」的想法,这篇文章便分享下我个人的一些学习技巧。

王国维在《人间词话》中提到了古今之成大事业、大学问者,必经的三种境界: “昨夜西风凋碧树,独上高楼,望尽天涯路。” 此第一境也。 “ 衣带渐宽终不悔,为伊消得人憔悴。” 此第二境也。 “ 众里寻他千百度蓦然回首,那人却在,灯火阑珊处。”此第三境也。我也按照我的理解,将精进技术分成了三个境界。

第一境: 从 overview 和 guides 获取新知识的直观感受

善用搜索,推荐 google。google 第一页搜索出来权威内容的概率要大于 baidu,人很容易有先入为主的观念,前五篇文章对一个知识点的直观感受,就会形成固化映像。( 技术精进的第一层关卡:你可能缺少一个梯子

以一个可能大多数读者都觉得陌生的知识点:图形数据库 Neo4j 作为引子,来介绍。

搜索 neo4j 关键词时大概率会搜索到官网,但作为一门技术的初学者,我更习惯于阅读下中文博客,教程,以对这个陌生的技术有一个宏观的了解。虽然大家都很赞同一个观点:英文文档更加权威,但是我个人还是觉得,中文教程更加直观,实际上我读中文文档的速度是英文文档的 2-3 倍。

w3cschool

不需要细读每个语法,我一般习惯看下概览,再看下目录对整体的知识点有个宏观的掌握。这种入门级的网站有很多,大多出现在搜索引擎的第一页。而官方的 overview 通常也是很适合入门的手段。官方网站

英文阅读能力强的读者可以直接上手官方的 guides,overview,get started 等入门教程,它们比中文教程强在很多方面 ( 技术精进的第二层关卡:你可能需要一定的英语阅读能力 ):

  1. 避免了语言转换带来的翻译失真。很多中文教程读起来总觉得拗口,太过于书面化,语句也不符合中国人阅读的习惯。翻译烂的原因不需要我总结,事实上我对翻译这个事有过比较深的真实体验,我曾经和 spring4all 的小伙伴们一起对 spring 文档进行过翻译,大家积极性都很高,一周之类几乎所有的 guides 文档全部翻译完成了,但质量真的惨不忍睹,参与校对时,竟然有人将 「jar」翻译成「蜜罐」,而且不在少数。
  2. 官方文档维护着最新的版本。对于一些热门的软件技术,比如我最近接触的 docker,之前研究过得 lucene,它们都一些共同点:版本差异非常大,版本更迭速度快。翻看 docker 在国内零散的博客,大多数比最新的 docker 版本落后 3 个大版本以上;而像 lucene 这种大版本演进非常迅速的技术(大版本演进意味着不兼容老版本),api 几乎是翻天覆地的变化。
  3. 权威性。

英语能力要求并不是很高,大学英语 4 级足矣,毕竟有个东西叫 谷歌翻译

再比如 spring 体系的知识点,有一个通用的学习路线。还是以 neo4j 为例,得知 spring 对 neo4j 有二次封装之后,便熟练地来到了 spring 的 guides 专栏 (https://spring.io/guides),spring 对所有的知识点提供了两个维度的学习文档,其中 https://spring.io/guides 一般都是一个 15 分钟上手的 hello world,让你快速上手一门新的技术;另一个是较为完善的文档:https://spring.io/docs/reference,成体系地介绍技术细节。第一阶段主要关注前者,一般它长这样:

guides

敲完 hello world,一般就可以对应简历上「了解 XXXX」的描述了 ( 斜眼笑。

第二境:官方文档与社区

大多数时候,一个停留在 hello world 认知级别的知识点对于我们的帮助不会很大,你甚至连在群里装逼的机会都没有!恐怖如斯!

了解完毕这门技术是用来解决什么问题的,它大概是怎么使用的,有了这些直观体验之后再来看文档,会直观不少。下面主要介绍个人阅读官方文档的一些心路历程。

首先来看看宇宙级开源项目 spring 的官方文档,它长这样:

mage-20180408153613

琳琅满目的项目,所有与 spring 相关的技术都可以在这儿获取最权威的解读,怎么学习 springboot,springcloud 还需要问吗?平时经常接触 spring 的同学如果这个页面都没见过,私以为在学习认知上是有所欠缺的。

refrence

文档除了主体知识内容之外还有整体介绍,新旧版本迭代的改动,新特性,依赖分析,性能测试等,文档内容一般都是非常多的,可以撷取其中核心的几节,将主要用法和注意事项掌握,至于不常用的特性,可以在碰到时再翻阅。

曾经一个项目需求中涉及到 spring security 的改造,而相关的文档又比较少,我至少通读过 5 遍 spring security 完整的文档,从一开始的大概了解,到最后的基本掌握,很多细节点一开始无法 get 到,读多了之后很多常用词汇和语感都会提升,细节会被消化,整体阅读速度也会随着文档阅读量提高而提升(比如 out-of-box 这个高频词一开始是不知道什么意思,谷歌翻译也翻不出来,后来得知是「开箱即用」)。

开源社区也是精进一门技术的重要途径,比如 spring4all,k8s,netty,elk 社区汇聚了不少文章和问答,初中高级的使用者都在社区中扮演着各自的角色(部分中文社区甚至样式都差不多,估计是用同一套开源代码搭建的 [捂脸]),社区活跃度也是一门技术火热的衡量指标。经常被大家调侃的面向 github 编程背后的 github,以及 Stack Overflow,都是质量比较硬的社区。

第三境:源码阅读

技术精进的第三层关卡:对源码的恐惧阻止了一个人探索的步伐 )如果你觉得看完前面的文字是在说废话,那么这一节可能稍微能勾起你的兴趣,权当之前是一个铺垫,照顾下一些初学者。

在交流群里面发现的一个现象,很多人对源码有一种天生的畏惧感,诸如:“我才刚毕业 / 我才工作三年,还没到看源码的阶段”,“源码不是架构师看的吗”,“源码看不懂“,”看看别人的源码分析不就行了“…这一节主要聊聊源码阅读技巧,以及一些个人对阅读源码的感悟。

首先澄清几点:阅读源码绝对和工作年限无关;阅读源码绝对和工作职位无关;大多数源码并不是很难;debug+ 源码分析绝对比看文章来的直观。

1 源码中的测试用例

还是以 neo4j 为例,我们在 github 找到 spring-data-neo4j 的源码,然后 git clone 到本地,在本地 idea 中打开。

test

和源码相关的第一点介绍的便是源码中的测试用例,对于大多数的开源项目而言,测试覆盖率是一个质量衡量的指标。大部分 Java 相关开源项目会包含测试用例,项目的一些功能特性可能在文档中无法一一介绍,通常可以在 src/test/java 中找到对应的用法,比如 neo4j 是怎么支持事务的,怎么维护边和边的关系的,在测试用例中都可以看到官方是怎么使用的。再举个例子,之前在使用 orika 这个拷贝工具时,一开始不知道怎么实现泛型的拷贝(泛型的运行时擦除特性),谷歌搜索和 Stack Overflow 提问无果之后,终于在源码的诸多测试用例中找到了我需要的代码。

2 通过核心接口 / 包结构分析框架层次结构

我猜测有人拒绝阅读源码的一个原因:源码注释量不够,压根不知道一段代码是干嘛用的。的确,我在阅读有些源码时也会出现这样的情况:这儿会什么要加锁?为什么要用 AtomicReference 这个类?为什么这个方法放在父类,而另外看似功能差不多的代码放在子类实现?框架编写者不会像培训班的老师一样跟你讲解他为什么要这么写,也不是所有的源码都能像 HashMap 的源码那样被大家泛滥地解读,是的,可能大多数情况下你的境地是「虽然不懂,还没法问!」气不气,尴不尴尬?没办法,因为这已经是第三境了,曲高和寡,但还是有些方法规避这样的情况的,那就是:主要关心核心接口,通过接口暴露的方法,猜测出作者的意图。据我不多的源码阅读经验,一个实现类的注释可能不多,但接口的注释通常会很多,毕竟一个原则是面向接口编程。

spring security

上图是我分析 spring security 源码时,根据接口间的关系整理出来的 UML 类图,对于绿色实现类的细节我可能并不是特别关注,浅蓝色代表的接口才是我们理解整个架构体系的切入点,配合 idea 这些优秀的集成开发环境,可以很方便的整理出 UML 类图。接口是全局架构,实现类是源码细节。

顺带一提:熟练使用 IDE 很重要。无论你是 eclipse 玩家还是 intellij idea 玩家,你都应该熟练掌握快捷键和一些常用操作,方便你阅读源码。比如 idea 右键可以自动生成类的继承关系图(聚合关系的体现不够智能),方便分析层次关系;显示方法 outline 快速查看一个类的方法概览,在成片的源码中非常有用;快速定位一个接口的实现类,一个类的子类等等。

再比如我在阅读 motan 这款 rpc 框架源码时遵循的顺序是其包结构的层次关系。

motan

写源码分析文章时基本就是按照一个包一篇文章来分析,从而化繁为简。无论是模块结构,包结构,还是代码层面的接口结构,重点都是在强调:我们需要从宏观掌握一个框架,再去扣细节,否则我个人感觉学习状态就是很迷,不知道学到哪儿了。

3 带着问题阅读实现类源码

具体的实现类源码真的没什么技巧可讲,一定是看一个人的写代码功底,以及代码敏感度了,非要说技巧的话,可能就是多写代码,培养代码敏感度了。此外,带着问题去读源码个人体验下来感觉不错,在阅读 spring security 源码时,我带着的问题是,怎么结合 zuul 实现动态的权限控制,一步步地 debug,看它原来的实现,之后是改源码,debug 看改变的效果。

具体的源码的阅读难度也是参差不齐的,个人学习经历中发现 motan 的源码就很容易阅读,spring 的源码因为文档比较齐全,阅读体验也很好,但 lucene 的源码和 hibernate 的源码,我也尝试阅读过,简直是天书,又比如说 netty,单单熟练使用它就已很难,何论源码。一方面跟个人阅历有关,一方面跟框架实现难度有关,很难盖棺定论得出方法论。个人建议是,明确自己需要解决什么问题去阅读源码,不必为了装逼而读源码。

贴下之前几个系列的源码解读链接:

【RPC 系列】 https://www.cnkirito.moe/categories/RPC/

【Spring Security 系列】https://www.cnkirito.moe/categories/Spring-Security/

【OAuth2 系列】https://www.cnkirito.moe/categories/Spring-Security-OAuth2/

三境之外

除了学习技术的三种境界,还有一些其他个人的感悟。比如类比学习法,一开始学习 spring-data-jpa 时效率比较慢,这对于我是一个比较新的技术,但当我后来再接触 spring-data-redis,spring-data-neo4j 时,虽然同样是第一次接触这些数据访问层,但有了之前 spring-data-jpa 的参考,可以说是事半功倍。关于视频,博客,书,文档可以说关系很微妙,从视频到文档,越来越不直观,但学习效率越来越高,这些没有高低贵贱之分,私以为都是很好的学习方法。怎么提升代码技巧?说真的方法论归方法论,重点还是代码行数锻炼出来的代码敏感度,这是看书,看代码,写博客,看方法论学不来的,不多说了,滚去写代码了 [抱拳]。

分享到