开发、自测、联调期间代码可能会被频繁地修改,一般即便只增长了一行代码,都须要重启容器以检查执行效果。而热部署技术可以帮助开发人员减小从新部署的等待时间。本文的目的为调研热部署的技术现状及其对开发效率的帮助,并简单梳理其技术实现的难点。html
JVM热部署目前有多种技术实现:官方、开源、商业。其中商业的JRebel功能强大,涵盖了平常开发中大部分热更新场景。以团队中一个基于Tomcat + Spring
的业务后台为例,修改代码后,本地冷启动耗时4.5min,本地热部署的时间则小于1s,极大改善了开发效率。java
当前JVM和JVMTI(JVM Tool Interface
)规范中经过相应的agent机制支持的retransformClass
/redefineClass
操做能够在加载前和加载后动态修改类的内容,从Java 5开始,这一功能还经过Instrumentation
API直接提供给Java应用使用,可是其适用范围是受限的:只能修改已有方法的方法体。git
如下摘自JVM(TM) Tool Interface 1.2.3
The redefinition may change method bodies, the constant pool and attributes. The redefinition must not add, remove or rename fields or methods, change the signatures of methods, change modifiers, or change inheritance. These restrictions may be lifted in future versions. See the error return description below for information on error codes returned if an unsupported redefinition is attempted.
IDE的edit-and-continue
功能(Intellij Idea
为Update Application
)就用到了这种被称为HotSwap
的热部署技术,可是它的限制太大,彻底没法知足实际开发中的需求。github
这是一个由JKU主导的、基于HotSpot VM的研究项目,诞生于2010年。该项目但愿能动态修改类的任意元素,包括成员、方法、注解、继承等而无需重启JVM。目前的light版已经支持到Java 8 update 144, build 2
。mvc
基于DCEVM构建的开源项目,其完成度要高于DCEVM,目前已发布1.0版。对于常见的IDE、IoC/ORM/Log框架、J2EE应用容器的支持比较完善。根据官方文档,HA支持下列特性。intellij-idea
Java世界中大名鼎鼎的热部署解决方案,热部署特性与上面提到的HotSwapAgent相似。固然做为一款商业软件,它支持的框架、IDE、J2EE应用容器的种类都更多,总计100+;同时支持Hotspot VM和Oracle VM;文档和社区支持很是完善,很容易上手。app
最重要的是,没钱的码农能够经过赞助官方的Social Plan
免费激活JRebel!cors
测试环境为团队使用的Tomcat + Spring + SpringMvc
。
如下是实际开发中常见的改动类型的测试结果。【Pass】为支持,【Fail】为不支持。框架
RequestHandler
方法、方法体、方法签名、注解<mvc:interceptors> / <mvc:cors> / <aop:aspectj-autoproxy> / <mvc:async-support>
等热部署的本质,简单的理解,是在运行中实时增长、替换JVM中的类文件而无需重启JVM。less
众所周知,JVM使用ClassLoader加载类文件,内含的双亲委派模型经过指定类文件加载的顺序避免因为类冲突而致使核心类库加载失败。单个ClassLoader不能加载全限定名相同的类;不能修改已加载的类的声明;不能卸载已加载的类,除非移除整个ClassLoader,或者被GC回收。
那么,修改原有的类(如Test.class
)的任意元素后,热部署就会面临不少问题。比方说:
Class
的getName()
、getMethods()
、getField()
等方法时如何获取到新的类?热部署问题在底层绕不开ClassLoader,当一个类被更新后,须要被从新载入到ClassLoader中,原先对类变量、实例变量、类方法、实例方法的调用都须要重定向到新类。能够经过引入一个包含全部符号连接的中间层,当JVM加载用户的类时进行动态加强,并记录下涉及的符号连接。
举几个实现思路的小例子:
Test
,而新的类名是Test_v1
),绕开ClassLoader的限制。refresh API