SpringBoot 热部署神器快速重启的秘密!

359次阅读  |  发布于2年以前

今天咱们来聊聊这个热部署神器 spring-boot-devtools 的运行原理,看看它是怎么用这个 ClassLoader 来实现快速重启,帮我们节省时间的!

文章概要

文章的主旋律如下

spring.factories

我们直接打开 spring-boot-devtools 源码 ,找到 spring.factories 文件:

我们一般都本地开发调试的,所以就直接看这个 LocalDevToolsAutoConfiguration 类啦

LocalDevToolsAutoConfiguration

可以看到核心点在 重启和重载

主角

我们先来看看这个 重启 中有什么叭

重启原理介绍

大概这么一个思路 下面就跟着源码分析啦(文末有源码重启要点流程图

RestartConfiguration

有这么些方法

从名字上分析,这两个方法应该是重点,逻辑上应该是 有一个 watcher 在盯着 classpath ,如果有变动的话,就触发这个 ClassPathChangedEvent 事件

那么看看这个 watcher

ClassPathFileSystemWatcher

可以看到这里就创建了这个 ClassPathFileSystemWatcher

这里我们注意到它实现了三个接口,经过前面 Spring 文章的学习,咱们知道第一步就该看啥了

根据类的初始化,先看看有 static 相关的代码没,接着看 构造器 ,最后就来到这个初始化方法 afterPropertiesSet

这里没有 static 方法,构造器也很简单,就是获取 FileSystemWatcherFactoryClassPathRestartStrategy 和 监视的文件路径,那么就看看 afterPropertiesSet 写了什么叭

ClassPathFileChangeListener

这个也不复杂,就监听到文件改变后,发布事件 ClassPathChangedEvent

FileSystemWatcher

接着就是这个 start 方法啦

很明显就是开启一个线程,那么咱们来看看线程中到底在 run 什么

找到这个任务类 Watcher

可以发现它的任务就是一直 scanpollInterval 默认是 1squietPeriod 默认是 0.4s

意思是每次轮询的时间是 1s ,包含中间休息的 0.4s ,休息事件是来确认文件在这个期间没有再次被改动。

改动了的话会回调 FileChangeListeneronChange ,对应我们上面的这个 ClassPathFileChangeListener ,会去发布事件 ClassPathChangedEvent

ApplicationListener

绕了一大圈,终于描述完了这个监视器 ClassPathFileSystemWatcher ,同时,我们也得把目光移到这个RestartConfiguration 的第二个核心 监听器

如图所示,这个方法的作用就是重启应用 restart

重启应用

重启的过程中呢,包括两个步骤,第一步 stop ,第二步 start

stop 部分就是毁灭这些东西了,这里也藏了很多细节,有很多并发相关的知识点

比如

一. ReentrantLock 是写在 try catch 的里面还是外面?

二. 循环里的 rootContexts 其实是 CopyOnWriteArrayList 类型的

三. 通过强制的 OOM 来清除所有的 软/弱引用 ( 还有这种操作的!)

start 的过程中,是通过创建这个重启线程 RestartLauncher 来实现的,可以发现该类的任务就是找到 mainclass 并调用 main 方法,完成重启。

而在这个过程中,就涉及到这个 classloader 啦。

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 事件机制 无处不在的身影,还有各种初始化的操作,以及线程,并发,锁在重启过程中的使用,这些就需要小伙伴们打开源码自身感受了,如 守护线程ReentrantLockCopyOnWriteArrayListCountDownLatch ,甚至 OOM 都能这么用!

还有 重启 原来就是 反射调用 main 方法

重启过程源码要点

本文就分享到这里啦,喜欢的朋友点个赞再走哦

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8