java热部署机制的实现方式简介

热部署和热加载定义

可以看到,热部署是包含热加载的,不过,这些概念并不太重要

热部署 hot deploy

热部署针对的是容器或者是整个应用,部署了新的资源或者修改了一些代码,需要在不停机的情况下的重新加载整个应用。

热加载 hotswap

热加载针对的是单个字节码文件,指的是重新编译后,不需要停机,应用程序就可以加载使用新的class文件

目前存在的方案

java hotswap

Java Platform Debugger Architecture是java IDE 调试的基础,
修改jvm启动参数即可打开,

1
-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n

技术原理也是java instrument api,
确定:仅能修改方法体,不能增加修改方法、field

另一种可行的思路

实现的思路是多种多样的,比如可以定制jvm,但是这里不讨论这些,这里只是讨论其中一种相对简单的实现

因为jvm中的class是不可变的,而且同一个classloader的同一个名字的class,jvm才会当作一个class,所以,不能直接去改变class,需要变通一下,用自定义的classloader
去加载新的class,同时业务上,需要去使用新的class,这样才可以变相的达到目的,

如何生成新的class

  1. 自己用字节码生成,相对硬核一些,不太友好,也不方便
  2. 用源码生成,相对简单,直接,更友好

自己使用字节码生成

java的库比较丰富,字节码框架还是非常多的,无论是基础的ASM,还是javaAssist,还是byteBuddy,都是各有千秋,适用于不同的场景,可以自行选择

用源码生成

  1. groovy编译java源码
  2. eclipse的ECJ
  3. java的JSR199的JavaCompilerAPI

groovy编译java源码

groovy是个好东西,尤其是当你需要动态特性的时候,没错,groovy本来写的编译器是用来编译groovy源码的,但是需要交叉编译,所以,顺带也会编译java的代码,所以,没错,groovy的compiler也会编译java的代码,没有任何的问题

eclipse的ECJ

eclipse本来是java的IDE,为了实现增量编译之类的一些特性吧,或者是别的特性,eclipse自己也开发了一套编译器,用来编译java的代码

java的JSR199的JavaCompilerAPI

这个就是直接的java api,原生的,相当于包裹了一下javac,效率不错

总结

groovy和ecj的方式非常简单,java的jsr199相对繁琐,但是原生,跟着jdk版本走,最有效,相对来说,可能性能也最好,兼容性也更好,但是需要JDK,你装一个JRE是不行的

如何加载新的class

前面提到的思路,已经说明了,java的同名类,类加载其必须是不同的,所以,每次加载,都需要一个新的classLoader,不过不用担心内存泄漏,java的class也是会内存回收的,原来不用的类,你只要切断所有的引用,这个类也是可以回收的

真的那么简单吗

其实讲到这里,技术的问题其实都已经解决了,但是类加载,不光光是技术上的问题,还有很多业务的问题,比如,类更改以后,如果让新的对象,全部派生自这个类,还有,尤其是对一些复杂的系统,比如spring,里面有相对复杂的初始化机制,问题就没那么简单,所以,
一旦类改变以后,你甚至还是触发一系列刷新的动作才可以,这里想表达的是,一旦类重新以后,jvm本质上是一个静态的系统,不是为动态设计的,所以,一旦出现动态以后,业务上差不多对是要做调整和兼容的,所以,使用的时候,想要和业务相结合

总结

本文,分析了一种热加载的技术,就是直接用别的classloader加载一个新的同名类,然后再使用业务上的方式让这个重新加载的类生效,同时,也顺带的分析了,如何生成一个新的类的几种方式,并做了简单的对比