首先来看看 JAVA 热部署与热加载的联系:
(1)都可以不重启服务器的情况下进行编译/部署项目;
(2)基于 Java 的类加载器实现
热部署与热加载的区别:
(1)热部署在服务器运行时重新部署项目
(2)热加载在运行时重新加载 class (字节码文件)
只加载重新修改后的类(class 文件)
(3)热部署会重新加载整个应用
(4)热加载在运行时重新加载 class
可以理解为 JVM 启动后会启动一个后台线程,定时来监控文件的时间戳,如果变化就将类重新载入
(5)热部署更多在生产环境下使用,热加载多在开发环境下使用(热加载无法记录“热加载执行的日志”)
下面再来说一下 JVM 加载类的相关知识点,字节码文件肯定是通过类加载器进行加载的,类加载一般可分为五个阶段:
(1)加载
找到类的静态存储结构,加载到虚拟机里然后转化成方法区运行时的数据结构,生成 class 对象;
允许用户自定义类加载器参与进来
(2)验证
确保字节码是安全的,不会对虚拟机造成危害,可以通过启动参数来禁用一些验证(不推荐)
(3)准备
确定内存布局,初始化类变量(给变量赋初始值不会执行程序自定义的赋值操作)
(4)解析
将符号引用转换为直接引用
(5)初始化
这里才是调用程序自定义的初始化代码
关于初始化阶段,Java 规定在遇到五个时机时立即进行初始化(当然前面的步骤已经执行完了的情况下),需要注意的点有:
(1)遇到了 new、get、static 这几个字节码指令时如果类没有初始化,则需要触发初始化。
(2)final 修饰的类会在编译时把结果放到常量池中,即使调用也不会触发初始化。毕竟 final 关键字它修饰的是常量。
(3)使用反射对类进行反射调用,如果类没有进行初始化,就需要先初始化。
(4)当初始化一个类的时候,如果发现其父类还没有进行过初始化,需要先触发父类的初始化。也就是先初始化父类,再初始化子类。
(5)虚拟机启动的时候用户需要制定一个要执行的主类,虚拟机会先初始化这个主类。
(6)使用 jdk1.7 动态机制相关的句柄会进行初始化。
Java 类加载器的特点:
(1)由 APPClass Loader (系统类加载器)开始加载指定的类。
(2)类加载器将加载任务交给其父,如果其父找不到,再由自己去加载。
(3)BootStrap Loader (启动类加载器)是最顶级的类加载器,也就是说他的父加载器为空。
Java 类的热部署可分为 类的热加载 和 配置 Tomcat 的方式,配置 Tomcat 应该很熟悉了,关于类的热加载相关代码参考:
// 自定义的类加载器
public class MyClassLoader extends ClassLoader{
private String path;
public MyClassLoader(String path){
super(ClassLoader.getSystemClassLoader());
this.path = path;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException{
System.out.println("加载类.....");
byte[] data = loadClassData(name);
return this.defineClass(name,data,0,data.length);
}
// 加载 Class 文件中的内容
private byte[] loadClassData(String name){
try{
name = name.replace(".", "//");
FileInputStream is = new FileInputStream(new File(path + name + ".class"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while((b = is.read()) != -1){
baos.write(b);
}
is.close();
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}
SpringBoot
使用 SpringBoot 进行热部署总体来说有两种方式,
一种是使用 springloaded(依赖配置在 build 中的 spring-boot-maven-plugin 插件中),必须要使用 mvn spring-boot:run 来允许才有效果,或者下载这个 jar 在 JVM 的启动参数里配置。
第二种就比较简单了,直接和平常一样加入一个 devtools 依赖就可以了:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
是的,就是这么简单,推荐使用第二种
发布
SpringBoot 项目可以使用 jar 包来直接运行,也可以发布为 war 丢到 tomcat 里去运行,
第一种就不用多说了,运行 maven 的 install 后,直接命令行启动就行:java -jar xxx.jar
第二种,首先打包方式改为 war 包,然后增加一个,然后在 application 入口类继承 SpringBootServletInitializer,复写 configure 方法:
// @SpringBootApplication:Spring Boot项目的核心注解,主要目的是开启自动配置
@SpringBootApplication
public class FirstSpringBootApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder){
return builder.sources(FirstSpringBootApplication.class);
}
public static void main(String[] args) {
// 启动 SpringBoot 所必须的入口
SpringApplication.run(FirstSpringBootApplication.class, args);
}
}
需要加入的依赖,SpringBoot 中加依赖不需要指定版本,在父工程已经设置好了,并且名称都是 spring-boot-* :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
然后执行 maven install 得到 war 包就可以了
热部署就是在运行时更新Java类文件。可以不重启应用的情况下,更新应用。
一般在系统中,类的加载都是由系统自带的类加载器完成,java类只能被加载一次,并且无法卸载。可以通过自定义类加载器,并重写ClassLoader的findClass方法,实现热部署。