什么是热部署
- 简单说就是你程序代码修改了,现在要重新启动服务器才能加载刚刚新写的代码,如果每次修改代码都要重新启动服务器,这样就真的太麻烦了(运行一些稍微较大的项目,运行时间是很长的,所以每次修改要重新运行就要等待很长的时间)。
修改代码后不用重启,服务器会自己悄悄的把更新后的程序给重新加载一遍,这就是热部署。
- 热部署的主要目的(针对学习):减少修改代码要重启服务的时间
- 热部署主要分两种情况:非springboot工程和springboot工程的热部署,这两种实现热部署的方式是不一样的。
1、非springboot项目热部署实现原理
-
开发非springboot项目时,正常情况我们要制作一个web工程并通过tomcat启动,通常需要先安装tomcat服务器到磁盘中,开发的程序配置发布到安装的tomcat服务器上。如果想实现热部署的效果,这种情况其实有两种做法,
- 一种是在tomcat服务器的配置文件中进行配置,这种做法与你使用什么IDE工具无关,不管你使用eclipse还是idea都行。
- 还有一种做法是通过IDE工具进行配置,比如在idea工具中进行设置,这种形式需要依赖IDE工具,每款IDE工具不同,对应的配置也不太一样。但是核心思想是一样的,就是使用服务器去监控其中加载的应用,发现产生了变化就重新加载一次。
-
非springboot项目实现热部署简单的思路(实际设计要比这复杂一些):例如启动一个定时任务,任务启动时记录每个文件的大小,以后每5秒比对一下每个文件的大小是否有改变,或者是否有新文件。如果没有改变,放行,如果有改变,刷新当前记录的文件信息,然后重新启动服务器,这就可以实现热部署了。当然,这个过程肯定不能这么做,比如我把一个打印输出的字符串"abc"改成"cba",比对大小是没有变化的,但是内容缺实变了,所以这么做肯定不行,只是给大家打个比方,而且重启服务器这就是冷启动了,不能算热部署,理解一下思路即可。
-
非Spring Boot项目的热部署实现原理,主要涉及到类加载器的自定义和动态替换机制(了解):
-
- 类加载器的作用:Java类的加载是由Java虚拟机(JVM)通过类加载器(ClassLoader) 来完成的。类加载器负责将.class文件的二进制数据加载到内存中,并进行链接(包括验证、准备和初始化等阶段),最终形成一个可以被JVM直接使用的Java类型。在JVM中,每个类只能被加载一次,并且由加载它的类加载器负责维护该类的唯一性。
-
- 热部署的需求:热部署是指在应用程序运行时,能够动态地替换或更新已加载的类文件,而无需重启整个应用程序。这对于提高开发效率、减少部署时间具有重要意义。
-
- 实现热部署的原理:在非Spring Boot项目中实现热部署,通常需要自定义类加载器,并通过某种机制来检测和替换已加载的类。以下是具体的实现步骤:
- 1、 自定义类加载器:由于JVM中每个类只能被其加载的类加载器唯一识别,因此要实现热部署,就需要创建一个新的类加载器来加载更新后的类文件。自定义类加载器通常需要继承自java.lang.ClassLoader类,并重写其findClass或loadClass方法,以便控制类的加载过程。
- 2、 检测和替换类文件:在自定义类加载器中,需要实现一种机制来检测和识别哪些类文件已经被更新。这可以通过文件系统的监听(可以实现一个监听器)、时间戳比较或其他方式来实现。一旦检测到类文件更新,就需要使用新的类加载器来加载这些类文件,并替换掉旧的类加载器及其加载的类。
- 3、 替换已加载的类:由于JVM的类加载机制,直接替换已加载的类是非常困难的。一种可行的方案是,**在应用程序中维护一个引用到自定义类加载器的映射表,当需要替换某个类时,通过该映射表找到对应的类加载器,并使用它来重新加载类文件。然而,这种方式需要应用程序的显式支持,并且可能会涉及到复杂的类加载和卸载逻辑。**另一种更为简单但可能不够彻底的方法是,在检测到类文件更新后,重启应用程序的某个部分或整个应用程序,以便使用新的类加载器来加载更新后的类文件。这种方法虽然简单,但可能会影响到应用程序的可用性和性能。
- 工具和框架的支持:虽然非Spring Boot项目没有像Spring Boot DevTools这样的内置热部署支持,但可以通过使用第三方工具或框架来实现类似的功能。例如,JRebel是一款商业化的Java开发工具,它提供了强大的热部署功能,可以在不重启JVM的情况下动态加载类的更改。
- 注意事项:热部署可能会带来一些潜在的问题,如类加载冲突、内存泄漏等,因此需要谨慎使用。
- 实现热部署的原理:在非Spring Boot项目中实现热部署,通常需要自定义类加载器,并通过某种机制来检测和替换已加载的类。以下是具体的实现步骤:
-
- 在生产环境中,通常不建议使用热部署,因为它可能会引入不可预测的行为和性能问题。在生产环境中,更推荐使用传统的部署方式,即重新构建和重新启动应用程序。
2、springboot项目热部署实现原理
- 基于springboot开发的web工程其实有一个显著的特征:就是tomcat服务器内置,
服务器是以一个对象的形式在spring容器中运行的
。- 本来我们期望于tomcat服务器加载程序后由tomcat服务器盯着程序,你变化后我就重新启动重新加载,但是现在tomcat和我们的程序是平级的了,都是spring容器中的组件,这下就麻烦了,缺乏了一个直接的管理权,那该怎么做呢?
- 简单,再搞一个程序X在spring容器中盯着你原始开发的程序A不就行了吗?确实,搞一个盯着程序A的程序X就行了,如果你自己开发的程序A变化了,那么程序X就命令tomcat容器重新加载程序A就OK了。并且这样做有一个好处,spring容器中东西不用全部重新加载一遍,只需要重新加载你开发的程序那一部分就可以了,这下效率又高了,挺好。
- 怎么搞出来这么一个程序X,肯定不是我们自己手写了,springboot早就做好了,搞一个坐标导入进去就行了。
2.1、手动启动热部署
步骤①:导入开发者工具对应的坐标(启动热部署的工具)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
步骤②:在修改代码后,点击该按钮构建项目(可以使用快捷键<CTR>L+<F9>
激活此功能)
-
底层原理(重启与重载)
- 一个springboot项目在运行时实际上是分两个过程进行的,根据加载的东西不同,划分成base类加载器与restart类加载器。
- base类加载器:用来加载jar包中的类,jar包中的类和配置文件由于不会发生变化,因此不管加载多少次,加载的内容不会发生变化。
- restart类加载器:用来加载开发者自己开发的类、配置文件、页面等信息,这一类文件受开发者影响
- 当springboot项目启动时,base类加载器执行,加载jar包中的信息后,restart类加载器执行,加载开发者制作的内容。当执行构建项目后,由于jar中的信息不会变化,因此base类加载器无需再次执行,所以仅仅运行restart类加载即可,也就是将开发者自己制作的内容重新加载就行了,这就完成了一次热部署的过程。(也可以说热部署的过程实际上是重新加载restart类加载器中的信息)
- 一个springboot项目在运行时实际上是分两个过程进行的,根据加载的东西不同,划分成base类加载器与restart类加载器。
-
手动启动热部署总结
-
- 使用开发者工具(添加依赖)可以为当前项目开启热部署功能
-
- 使用构建项目操作对工程进行热部署
-
-
上述过程每次进行热部署都需要开发者手工操作,不管是点击按钮还是快捷键都需要开发者手工执行。这种操作的应用场景主要是在开发调试期,并且调试的代码处于不同的文件中,比如服务器启动了,我需要改4个文件中的内容,然后重启,等4个文件都改完了再执行热部署,使用一个快捷键就OK了。
- 但是如果现在开发者要修改的内容就只有一个文件中的少量代码,这个时候代码修改完毕如果能够让程序自己执行热部署功能,就可以减少开发者的操作,也就是自动进行热部署。
2.2、自动启动热部署
- 自动热部署其实就是设计一个开关,打开这个开关后,IDE工具就可以自动热部署。因此这个操作和IDE工具有关,以下以idea为例设置idea中启动热部署:
-
步骤①:设置自动构建项目
- 打开【File】,选择【settings…】,在面板左侧的菜单中找到【Compile】选项,然后勾选【Build project automatically】:
- 打开【File】,选择【settings…】,在面板左侧的菜单中找到【Compile】选项,然后勾选【Build project automatically】:
-
步骤②:允许在程序运行时进行自动构建
- 使用快捷键【Ctrl】+【Alt】+【Shit】+【/】打开维护面板,选择第1项【Registry…】(这个是原来的寻找方法,已经找不到compiler.automake.allow.when.app.running这个选项了)
- 使用快捷键【Ctrl】+【Alt】+【Shit】+【/】打开维护面板,选择第1项【Registry…】(这个是原来的寻找方法,已经找不到compiler.automake.allow.when.app.running这个选项了)
勾选上上面的那个框,这样程序在运行的时候就可以进行自动构建了,实现了热部署的效果。
-
问题1:如果你每敲一个字母,服务器就重新构建一次,这未免有点太频繁了,所以idea设置当idea工具失去焦点5秒后进行热部署。其实就是你从idea工具中切换到其他工具时进行热部署,比如改完程序需要到浏览器上去调试,这个时候idea就自动进行热部署操作。
-
问题2:现在已经实现了热部署了,但是到企业开发的时候你会发现,为了便于管理,在你的程序目录中除了有代码,还有可能有文档,如果你修改了一下文档,这个时候会进行热部署吗?不管是否进行热部署,这个过程我们需要自己控制才比较合理。
2.3、参与热部署监控的文件范围配置
- 通过修改项目中的文件,你可以发现其实并不是所有的文件修改都会激活热部署的,原因在于在开发者工具中有一组配置,当满足了配置中的条件后,才会启动热部署,配置中
默认不参与热部署的目录信息
如下
- /META-INF/maven
- /META-INF/resources
- /resources
- /static
- /public
- /templates
以上目录中的文件如果发生变化,是不参与热部署的。如果想修改配置,可以通过application.yml文件进行设定哪些文件不参与热部署操作
spring:
devtools:
restart:
# 设置不参与热部署的文件或文件夹
exclude: static/**,public/**,config/application.yml
- 热部署监控的文件范围配置:1. 通过配置可以修改不参与热部署的文件或目录
- 问题:热部署功能是一个典型的开发阶段使用的功能,到了线上环境运行程序时,这个功能就没有意义了。能否关闭热部署功能呢?答案是肯定的,线上环境运行时是不可能使用热部署功能的,所以需要强制关闭此功能。
2.4、关闭热部署
- 线上环境运行时是不可能使用热部署功能的,所以需要强制关闭此功能,通过配置可以关闭此功能。
spring:
devtools:
restart:
enabled: false
- 如果担心配置文件层级过多导致相符覆盖最终引起配置失效,可以提高配置的层级,在更高层级中配置关闭热部署。例如在启动容器前通过系统属性设置关闭热部署功能。
@SpringBootApplication
public class SSMPApplication {
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled","false");
SpringApplication.run(SSMPApplication.class);
}
}
其实上述担心略微有点多余,因为线上环境的维护是不可能出现修改代码的操作的,这么做唯一的作用是降低资源消耗,毕竟那双盯着你项目是不是产生变化的眼睛只要闭上了,就不具有热部署功能了,这个开关的作用就是禁用对应功能。
- 如何关闭热部署(总):通过配置可以关闭热部署功能降低线上程序的资源消耗