为什么需要热部署
在实际开发中,经常要修改代码,然后重启服务,再验证代码是否生效。对于开发场景,随着项目的演进,微服务越来越多,等待重启的时间也会越来越多;对于联调场景,对一处功能的修改后通知上游联调,同样会浪费大量的碎片化时间。热部署的出现就帮我们解决了这些痛点,所谓热部署,就是在应用正在运行时升级软件,却不需要重新启动应用,它能极大的提高开发效率
热部署的方式
IDEA HotSwap
从Java1.4起,JVM引入了HotSwap,能够在Debug的时候更新类的字节码。所以使用热部署,可以实现修改代码后,无须重启服务就可以加载修改的代码,但是它只能用来更新方法体
与其说HotSwap是热部署,不如说它是热加载,首先它只能工作在debug模式下,其次JVM只能实现方法体修改的热加载,对于整个类的修改仍然需要重启JVM,对类重新加载
要使用HotSwap,项目配置如下
修改方法体后,触发idea的build,会提示类被重新加载
Spring Boot Devtools
spring-boot-devTools严格意义上也不能算是热部署,而是快速重启。它的实现原理是:使用两个类加载器,一个是base classloader来加载不会被更改的类(例如第三方Jar),还有一个是restart classloader用来加载当前正在开发的类。当应用程序重新启动时,restart classloader将被丢弃,并创建一个新的类加载器
为了确定重启的时机,spring-boot-devTools通过监控类路径资源,当类路径上的文件发生更改时,自动重新启动应用程序,由于只需要重新读取被修改的类,所以要比冷启动快得多,因为base classloader已经填充好了并且是可用的
它的优势在于不再局限于debug模式,也支持对本地代码的方法体、类结构修改。同时它支持了对spring boot、spring validator、mybatis、cglib、logback等三方插件的热部署
它的劣势在于只能应用在spring boot项目中,并且由于通过ClassLoader迭代的方式重启项目,效率比较低
要使用spring-boot-devTools,首先需要引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
为了避免性能浪费,我通常选择手动触发重启,因此关闭idea的自动编译
配置好后,对项目文件做修改再触发idea build,就会触发spring-boot-devTools对项目重启
顺带一提,如果在debug模式下使用spring-boot-devTools,并且对SpringApplication的run方法做了try/catch,会catch到一个异常,但并不影响使用,这是一个新的已知问题。参考这条stackoverflow上的回答:java - Breakpoint at "throw new SilentExitException()" in Eclipse + Spring Boot - Stack Overflow
参考
SpringBoot实现热部署两种方式! - 掘金
Java系列 | 远程热部署在美团的落地实践 - 美团技术团队