今天咱们来聊聊这个热部署神器 spring-boot-devtools 的运行原理,看看它是怎么用这个 ClassLoader 来实现快速重启,帮我们节省时间的!
文章的主旋律如下
我们直接打开 spring-boot-devtools 源码 ,找到 spring.factories 文件:
我们一般都本地开发调试的,所以就直接看这个 LocalDevToolsAutoConfiguration 类啦
可以看到核心点在 重启和重载
我们先来看看这个 重启 中有什么叭
大概这么一个思路 下面就跟着源码分析啦(文末有源码重启要点流程图)
有这么些方法
从名字上分析,这两个方法应该是重点,逻辑上应该是 有一个 watcher 在盯着 classpath ,如果有变动的话,就触发这个 ClassPathChangedEvent 事件
那么看看这个 watcher 叭
可以看到这里就创建了这个 ClassPathFileSystemWatcher 类
这里我们注意到它实现了三个接口,经过前面 Spring 文章的学习,咱们知道第一步就该看啥了
根据类的初始化,先看看有 static 相关的代码没,接着看 构造器 ,最后就来到这个初始化方法 afterPropertiesSet 啦
这里没有 static 方法,构造器也很简单,就是获取 FileSystemWatcherFactory ,ClassPathRestartStrategy 和 监视的文件路径,那么就看看 afterPropertiesSet 写了什么叭
这个也不复杂,就监听到文件改变后,发布事件 ClassPathChangedEvent
接着就是这个 start 方法啦
很明显就是开启一个线程,那么咱们来看看线程中到底在 run 什么
找到这个任务类 Watcher
可以发现它的任务就是一直 scan ,pollInterval 默认是 1s ,quietPeriod 默认是 0.4s
意思是每次轮询的时间是 1s ,包含中间休息的 0.4s ,休息事件是来确认文件在这个期间没有再次被改动。
改动了的话会回调 FileChangeListener 的 onChange ,对应我们上面的这个 ClassPathFileChangeListener ,会去发布事件 ClassPathChangedEvent
绕了一大圈,终于描述完了这个监视器 ClassPathFileSystemWatcher ,同时,我们也得把目光移到这个RestartConfiguration 的第二个核心 监听器
如图所示,这个方法的作用就是重启应用 restart
重启的过程中呢,包括两个步骤,第一步 stop ,第二步 start
stop 部分就是毁灭这些东西了,这里也藏了很多细节,有很多并发相关的知识点
比如
一. ReentrantLock 是写在 try catch 的里面还是外面?
二. 循环里的 rootContexts 其实是 CopyOnWriteArrayList 类型的
三. 通过强制的 OOM 来清除所有的 软/弱引用 ( 还有这种操作的!)
在 start 的过程中,是通过创建这个重启线程 RestartLauncher 来实现的,可以发现该类的任务就是找到 mainclass 并调用 main 方法,完成重启。
而在这个过程中,就涉及到这个 classloader 啦。
细心的小伙伴可以发现上面这行代码中,调用到了这个 ClassLoader ,这个 getContextClassLoader() 是属于 Thread 类的,通过它可以获取到当前线程上下文的 ClassLoader 。
Class.forName(this.mainClassName, false, getContextClassLoader());
在创建这个 RestartLauncher 线程时,就已经将咱们这个 RestartClassLoader 给传进来了。
重启时,就直接通过 RestartClassLoader 去找到 main 方法,完成重启。
很明显这里 破坏了双亲委派机制,先从自身查找,没有的话再去父类查找
这里 业务代码 都被 RestartClassLoader 加载了,而每次重启都会重新创建这个 RestartClassLoader ,然后去加载业务代码 (通过传进来的 URL 可以发现)
那么到此,这个 重启 的过程就完成了。
差点忘了,这里还有个默认的监视范围
如下图 默认策略中,这些路径下的文件变化不被检测
image-20210920230435492
可通过配置修改
spring.devtools.restart.exclude=static/**,public/**
通过阅读源码,我们知道了 spring-boot-devtools 是通过自定义 RestartClassLoader 来加载业务代码,并在重启时销毁它,再重新创建,进而重新获取代码,实现这个快速重启的。
而其他 jar 包等由另外的 ClassLoader 加载,不受影响。
同时,也可以看到 Spring 事件机制 无处不在的身影,还有各种初始化的操作,以及线程,并发,锁在重启过程中的使用,这些就需要小伙伴们打开源码自身感受了,如 守护线程,ReentrantLock ,CopyOnWriteArrayList ,CountDownLatch ,甚至 OOM 都能这么用!
还有 重启 原来就是 反射调用 main 方法 呀
本文就分享到这里啦,喜欢的朋友点个赞再走哦
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8