开心一刻
今天上课不小心睡着了,结果被老师叫起来回答问题,这是背景
无奈之下看向同桌寻求帮助,同桌小声说到选C
,结果周围的人都说选C
我向同桌投去一个感激的眼神后大声说道:选C
刚说完教室就笑开了
老师一脸恨铁不成钢的表情说:选你个头,我叫你翻译文言文你选C
!你出去,你给我出去
看着同桌挤眉弄眼的表情,劳资真想说,这帮畜生!
互相抱怨道:你是不是又长胖了?
读源码的经历
刚参加工作那会,没想过去读源码,更没想过去改框架的源码
总想着别人的框架应该是完美的、万能的,应该不需要改
另外即使我改了源码,怎么样让我的改动生效了,项目中引用的不还是没改的jar包吗?
回想起来觉得那时候的想法确实挺......
工作了一年多之后准备跳槽了,开始了一轮的面试
其中有几个面试官就问到了相关的源码问题:ArrayList
、HashMap
的底层实现,spring
、mybatis
的相关源码
问源码的面试一般就是回去等消息,然后就没然后了
那时候开始意识到,源码这东西在之前的工作的中感受不到,但是在面试中好像面的还挺频繁的,从此有计划的开始了jdk
部分源码的阅读(主要是集合)
一开始看源码,看的特别糙,知道个大概,知道ArrayList
的底层实现是数组,HashMap
的底层是散列表(数组+链表)
更深入一点的扩容、hash
碰撞等等就不知道了
读Spring
源码始于工作中遇到了一个问题(spring jdbcTemplate事务,各种诡异,包你醍醐灌顶!)
虽说经过一段时间的排查,最终是解决了,但过程让我非常难受
网上各种查资料、各种尝试,感觉就像大海捞针一样,遥遥无期
我暗下决心,我要看一看Spring
的源码,于是我买了一本《spring源码深度解析》
,结合着这本书、打开着eclipse
,开始了Spring
的源码阅读之旅
至此,读源码成了习惯,源码已走进了我的心里!
后来,Spring Boot
的火热,让我也想蹭上一蹭,于是有了Spring Boot
的启动源码系列
工作中用到了Shiro
,我又结合着《跟我学Shiro》
将Shiro
的源码看了个大概,有了Shiro源码系列
最近在搭建自己的后台管理系统,用到了Quartz
,集成的过程也遇到了一些问题,因此有了quartz的三篇文章
慢慢的,从一味的在网上找资料变成了很多时候会从源码中找答案
不求能读太多的源码,但愿自己接触的技术都能读上一读,路漫漫其修远兮,吾将上下而求索!
为什么读源码
相信很多人和我一样,有这样的的疑问:我就一个写代码的,需要读源码吗?
但慢慢的,我改变了这样的认知
一刚开始为了面试,后来为了解决工作中的问题,再后来就是个人喜好了
说的好听点是有匠人精神
说的委婉点是好奇(底层是怎么实现的?)
说的不自信点是对黑盒的东西我用的没底,怕用错
说的简单直白点是提升自我价值,为了更高的薪资待遇(这里对真正的技术迷说声抱歉)
源码中我们可以学到很多东西:高效的写法、设计模式的使用、架构的设计与实现,等等
如果你还能找出其中的bug
,那么恭喜你,你升级了!
会使用固然重要,但知道为什么这么使用同样重要,知其然,知其所以然!
从模仿中学习,从模仿中创新!
读源码不像围城(外面的人想进来,里面的人想出去),它是外面的人不想进来,里面的人不想出去
当我们跨进城内,你会发现城内风光无限,源码的海洋任我们遨游!
你想好入城了吗?
怎么样读源码
精通内容
首先我们要对我们的目标有所了解,知道她有什么特点,有哪些爱好
对对方都还不了解,就想着闯入人家的内心,那不是耍流氓,臭不要脸嘛
我们要做一个有着流氓心的绅士,对她有了清晰的了解后,再可以发起攻势,一举拿下
那么怎么样了解她,方式有很多,我这里提供几种,大家可以参考:
1、官方参考指南
,亲生父母往往对孩子是最了解的,对孩子的描述也是最详细的
比如Spring Boot Reference Guide就是对Spring Boot
最详细的描述
她有哪些特性,如何与她友好相处等等,此指南都有详细记载
熟读此指南,她将犹如刚出浴的美人,在你面前一丝不挂,不对,是一览无遗!
但是,Spring Boot
毕竟是外国人的孩子,如果英语不好,估计读起来有点头疼了,不过我们有google
翻译呀,咬咬牙也是能看的
源码世界的丈母娘、老岳丈是非常慷慨的!
2、书籍
,国外有很多优秀书籍,国内也有很多优秀书籍,比较推荐此方式,自成体系,让我们掌握的知识点不至于太散
这就是好比是源码的闺蜜,对源码非常了解,重点是挺大方,会尽全力帮助我们了解她
3、博客
,知识点可能比较单一,但是很有针对性,对彻底掌握某个知识点非常有帮助
比较负责的博主一般会写系列,前后串联起相关的知识点,让大家很容易就能看懂
很明显,楼主就是这样的博主!
掌握设计模式
优秀的框架、技术从不乏设计模式
jdk
源码中就应用了很多设计模式,例如IO
流中的适配器模式
与装饰模式
、GUI
的观察者模式
、集合中的迭代器模式
,等等
Spring
源码中也是用到了大量的设计模式,例如事件机制中的观察者模式
、AOP
的核心动态代理
、JdbcTemplate
以及RestTemplate
涉及的模板方法
,等等
设计模式有什么优点、各适用于什么场景,不是本文的内容,你们自行去查阅
我们只需要对一些常用的设计模式有个大致掌握,再去读源码是比较好的
不需要精通23
种设计模式,毕竟薛之谦
说过:其实感情最怕的就是拖着
推荐书籍:《Head First Design Patterns》
(中文版:《Head First 设计模式》
)、《Java与模式》
设计模式之于源码,就好比逛街购物之于女人,想顺利勾搭源码,我们需要好好掌握设计模式这个套路
了解断点调试
通过源码的圈子对她的了解,终究只是停在表面,始终未能走进她的内心,接下来我就和大家分享下,我是如何走进她的内心的!
相信看过我的源码博客的小伙伴都知道,我非常喜欢通过idea
断点来进行源码追踪,断点追踪源码是我非常推荐的一种方式
断点不仅可以用来调试我们的代码,也可以用来调试我们用到的框架源码
面对未知的、茫茫多的源码,我们往往没有足够的时间、精力和耐心去通读所有源码,我们只需要去读我们关注的部分即可(都看到这了,就不要说:我都不关心了)
为什么要用断掉调试的方式来跟源码,而不是直接从源代码入手去跟我们关注的部分呢?
尝试过的小伙伴应该知道,如果我们对源码不熟悉,直接通过源码的方式去跟,很容易迷路(多态,会有很多子类实现),不知道接下来跟哪一个,当我们跟入的很深的时候,很有可能又忘记上一步跟到哪了
下面我举例来说明我是如何进行断点追踪的,以spring-boot-2.0.3之quartz集成,不是你想的那样哦!和 spring-boot-2.0.3之quartz集成,数据源问题,源码探究 为背景来讲
讲清楚两个点:1、springboot
是如何向quartz
注入数据源的,2、quartz
是如何操作数据库的
我们先来看第1点,QuartzAutoConfiguration
是springboot
自动配置quartz
的入口
将quartz
的配置属性设置给SchedulerFactoryBean
将数据源设置给SchedulerFactoryBean
:如果有@QuartzDataSource
修饰的数据源,则将@QuartzDataSource
修饰的数据源设置给SchedulerFactoryBean
,否则将应用的数据源(druid
数据源)设置给SchedulerFactoryBean
显然我们的应用中没有@QuartzDataSource
修饰的数据源,那么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
,没事务
我们再来看第2点,通过停止定时任务来跟下quartz
对数据库的操作
发现quartz
用如下方式获取connection
conn = DBConnectionManager.getInstance().getConnection(getDataSource());
那么我们的job
中就可以按如下方式操作数据库了
package com.lee.quartz.job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.utils.DBConnectionManager;
import org.springframework.scheduling.quartz.LocalDataSourceJobStore;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
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注入进来
@Override
protected 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;
}
}
明确我们的目的,找到合适的切入点,进入断点调试追踪也就容易了
任我说的天花乱坠,你仍无动于衷,那也只是我一厢情愿,只有局中人才能体会到其中的奥妙!
言外之意就是你得调试起来!
断点调试
就好比软妹币
,能够省去很多中间环节,让你直达她的内心!
总结与感悟
从上至下全部通读的方式,个人不太推荐,这是建立在很熟悉的基础上的
当我们对某个框架已经比较熟悉了,再从上至下进行通读,彻底了解,这是我认为正确的方式
但是从不熟悉到熟悉这个过程,个人不推荐全部通读,而是推荐上面我推荐的方式:断点调试
很多时候,我们的博文都只是授之以鱼,而我们也只是从中得到鱼
而这篇的目的则是授之以渔,我希望大家从中学到捕鱼的方法,而不是一味的等待别人的鱼
希望大家能够自给自足,也能把鱼和渔都授予其他人
只要我们开始去读源码,慢慢的就会形成自己的一套读源码的方式
每个人的方式都不一样,适合自己的才是最好的
行动起来,用合适的方式去俘获你的她吧!