深入探索Java热部署

简介

在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现 方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作。 对于某些大型的应用来说,每次的重启都需要花费大量的时间成本。虽然 osgi 架构的出现,让模块重启 成为可能,但是如果模块之间有调用关系的话,这样的操作依然会让应用出现短暂的功能性休克。本文将 探索如何在不破坏 Java 虚拟机现有行为的前提下,实现某个单一类的热部署,让系统无需重启就完成某 个类的更新。

类加载的探索

首先谈一下何为热部署(hotswap),热部署是在不重启 Java 虚拟机的前提下,能自动侦测到 class 文件的变化,更新运行时 class 的行为。Java 类是通过 Java 虚拟机加载的,某个类的 class 文件在被 classloader 加载后,会生成对应的 Class 对象,之后就可 以创建该类的实例。默认的虚拟机行为只会在启动时加载类,如果后期有一个类需要更新的话,单纯替换 编译的 class 文件,Java 虚拟机是不会更新正在运行的 class。如果要实现热部署,最根本的方式是修 改虚拟机的源代码,改变 classloader 的加载行为,使虚拟机能监听 class 文件的更新,重新加载 class 文件,这样的行为破坏性很大,为后续的 JVM 升级埋下了一个大坑。

另一种友好的方法是 创建自己的 classloader 来加载需要监听的 class,这样就能控制类加载的时机,从而实现热部署。本 文将具体探索如何实现这个方案。首先需要了解一下 Java 虚拟机现有的加载机制。目前的加载机制,称 为双亲委派,系统在使用一个 classloader 来加载类时,会先询问当前 classloader 的父类是否有能力 加载,如果父类无法实现加载操作,才会将任务下放到该 classloader 来加载。这种自上而下的加载方 式的好处是,让每个 classloader 执行自己的加载任务,不会重复加载类。但是这种方式却使加载顺序 非常难改变,让自定义 classloader 抢先加载需要监听改变的类成为了一个难题。

不过我们可以 换一个思路,虽然无法抢先加载该类,但是仍然可以用自定义 classloader 创建一个功能相同的类,让 每次实例化的对象都指向这个新的类。当这个类的 class 文件发生改变的时候,再次创建一个更新的类 ,之后如果系统再次发出实例化请求,创建的对象讲指向这个全新的类。

下面来简单列举一下需 要做的工作。

创建自定义的 classloader,加载需要监听改变的类,在 class 文件发生改变的时 候,重新加载该类。

改变创建对象的行为,使他们在创建时使用自定义 classloader 加载的 class。

自定义加载器的实现

自定义加载器仍然需要执行类加载的功能。这里却存在一个 问题,同一个类加载器无法同时加载两个相同名称的类,由于不论类的结构如何发生变化,生成的类名不 会变,而 classloader 只能在虚拟机停止前销毁已经加载的类,这样 classloader 就无法加载更新后的 类了。这里有一个小技巧,让每次加载的类都保存成一个带有版本信息的 class,比如加载 Test.class 时,保存在内存中的类是 Test_v1.class,当类发生改变时,重新加载的类名是 Test_v2.class。但是真 正执行加载 class 文件创建 class 的 defineClass 方法是一个 native 的方法,修改起来又变得很困 难。所以面前还剩一条路,那就是直接修改编译生成的 class 文件。

利用 ASM 修改 class 文件

可以修改字节码的框架有很多,比如 ASM,CGLIB。本文使用的是 ASM。先来介绍一下 class 文 件的结构,class 文件包含了以下几类信息,一个是类的基本信息,包含了访问权限信息,类名信息,父 类信息,接口信息。第二个是类的变量信息。第三个是方法的信息。ASM 会先加载一个 class 文件,然 后严格顺序读取类的各项信息,用户可以按照自己的意愿定义增强组件修改这些信息,最后输出成一个新 的 class。

首先看一下如何利用 ASM 修改类信息。

清单 1. 利用 ASM 修改字节码

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassReader cr = null;
String enhancedClassName = classSource.getEnhancedName();
try {
    cr = new ClassReader(new FileInputStream(
            classSource.getFile()));
} catch (IOException e) {
    e.printStackTrace();
    return null;
}
ClassVisitor cv = new EnhancedModifier(cw,
        className.replace(".", "/"),
        enhancedClassName.replace(".", "/"));
cr.accept(cv, 0);

ASM 修改字节码文件的流程是一个责任链模式,首先使用一个 ClassReader 读入字节码,然后利用 ClassVisitor 做个性化的修改,最后利用 ClassWriter 输出修改 后的字节码。

之前提过,需要将读取的 class 文件的类名做一些修改,加载成一个全新名字的派 生类。这里将之分为了 2 个步骤。

第一步,先将原来的类变成接口。

清单 2. 重定义的 原始类

   public Class<?> redefineClass(String className){
       ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
       ClassReader cr = null;
       ClassSource cs = classFiles.get(className);
       if(cs==null){
           return null;
       }
       try {
           cr = new ClassReader(new FileInputStream(cs.getFile()));
       } catch (IOException e) {
           e.printStackTrace();
           return null;
       }
       ClassModifier cm = new ClassModifier(cw);
       cr.accept(cm, 0);
       byte[] code = cw.toByteArray();
       return defineClass(className, code, 0, code.length);
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索文件
, class
, classloader
, 更新
, 类名
, 热部署
, 热部署 java web 运维
, class无法加载
, 热编译
, 类加载
, 一个
, 加载监听
, 热替换
自动加载类实现
深入探索c 对象模型、深入探索android、深入探索透视投影变换、深入探索、对 深入部署,以便于您获取更多的相关知识。

时间: 2024-10-03 11:21:29

深入探索Java热部署的相关文章

深入探索 Java 热部署

在 JAVA 开发领域,热部署一直是一个难以解决的问题,目前的 JAVA 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作.对于某些大型的应用来说,每次的重启都需要花费大量的时间成本.虽然 OSGI 架构的出现,让模块重启成为可能,但是如果模块之间有调用关系的话,这样的操作依然会让应用出现短暂的功能性休克.本文将探索如何在不破坏 JAVA 虚拟机现有行为的前提下,实现某个单一类的热部署,让系统无需重启就完成某个类的更新. 类加载的探索 首先

如何在不破坏Java虚拟机的前提下实现热部署

本文将探索如何在不破坏 Java 虚拟机现有行为的前提下,实现某个单一类的热部署,让系统无需重启就完成某个类的更新. 在 Java 开发领域,热部署一直是一个难以解决的问题,目前的 Java 虚拟机只能实现方法体的修改热部署,对于整个类的结构修改,仍然需要重启虚拟机,对类重新加载才能完成更新操作.对于某些大型的应用来说,每次的重启都需要花费大量的http://www.aliyun.com/zixun/aggregation/7201.html">时间成本.虽然 osgi 架构的出现,让模块

服务器-JAVA 关于发布JAR包后热部署问题

问题描述 JAVA 关于发布JAR包后热部署问题 目前有2台服务器,负载均衡,2台服务器上分别部署了WINDOWS后台服务程序, 修改了JAR包后,可以不重启服务重新加载吗?万分感谢! 解决方案 方法: 1. 先了解类加载器,编写自定义类加载器 2. OSGI技术,已经有很规范的技术实现了 解决方案二: 修改tomcat的 server.xml 设置 reloadable="true" ,重启后即可!

Java服务器热部署的实现原理

[本文转载于Java服务器热部署的实现原理] 今天发现早年在大象笔记中写的一篇笔记,之前放在ijavaboy上的,现在它已经访问不了了.前几天又有同事在讨论这个问题.这里拿来分享一下. 在web应用开发或者游戏服务器开发的过程中,我们时时刻刻都在使用热部署.热部署的目的很简单,就是为了节省应用开发和发布的时间.比如,我们在使用Tomcat或者Jboss等应用服务器开发应用时,我们经常会开启热部署功能.热部署,简单点来说,就是我们将打包好的应用直接替换掉原有的应用,不用关闭或者重启服务器,一切就是

热部署 java web 运维-在部署web应用的时候,怎么才能不影响正常用户的访问实现热部署?比较主流的方案是怎么样的

问题描述 在部署web应用的时候,怎么才能不影响正常用户的访问实现热部署?比较主流的方案是怎么样的 在部署web应用的时候,怎么才能不影响正常用户的访问实现热部署?比较主流的方案是怎么样的.假如我只有一台服务器 有没有必要在一台服务器上配置两个web应用来提高服务的可用性 解决方案 负载均衡,多台服务器备份,然后部署的时候,先部署一台,把流量都导到另一台处理业务,等部署好后,开始逐步分配流量,进行监测看业务是否运行正常 如果没什么问题,那么就进行另一台的部署,同样的流程 解决方案二: 负载均衡,

tomcat 和 jboss的热部署(热发布)问题

所谓的热部署(热发布)(下面称为"热部署"),就是说,在web工程发布之后,不可避免的,会遇到修改BUG的问题.现在的热部署就是为了解决这个问题,其功能就是说:在不停止web服务的同时,对jsp和java类进行修改,修改后的效果同时还能够在页面上显示出来.节省了调试时间,提高了效率.不过,修改配置文件是个例外,如果对配置文件做修改,一定要重启web服务.         常用的web服务器一般为tomcat和jboss,现一一做介绍.         1.tomcat热部署      

spring-boot 速成(2) devtools之热部署及LiveReload

JRebel热部署插件相信很多人都知道,但是这是一款商业插件,spring-boot框架也提供了类似的功能,即:devtools,关键是免费的! 使用方法如下: 一.添加 devtools依赖 dependencies { compile('org.springframework.boot:spring-boot-starter-web') compile('org.springframework.boot:spring-boot-devtools') compileOnly('org.proj

Tomcat热部署和虚拟目录配置

1.Tomcat如何配置热部署 默认就是  1 <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true"> 如果要实现IDE修改代码的自动部署,可以使用第三方工具 http://www.zeroturnaround.com/jrebel/ 1.解压缩包 2.执行java -jar jrebel-setup.jar 3.会

ODM规则执行服务器RES支持热部署的两种方式

规则集的热部署是指在规则集版本发生变化时,规则执行组件 (XU) 可以自动接收到规则集版本更新的通知 , 从而自动加载最新的规则集,整个过程用户不需要重启任何组件.业务规则管理系统实现了业务规则和应用程序逻辑的分离,业务用户可以在业务逻辑放生变化时,即时的更新业务规则,部署最新的规则集到规则执行服务器.而规则集的热部署则保证了在业务逻辑发生变化时,业务规则管理http://www.aliyun.com/zixun/aggregation/18477.html">系统服务的连续性. ODM