`
chenhua_1984
  • 浏览: 1255207 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

Java 类的热替换

    博客分类:
  • java
阅读更多

       在 Java 中,类的实例化流程分为两个部分:类的加载和类的实例化。类的加载又分为显式加载和隐式加载。大家使用 new 关键字创建类实例时,其实就隐式地包含了类的加载过程。对于类的显式加载来说,比较常用的是 Class.forName。其实,它们都是通过调用 ClassLoader 类的 loadClass 方法来完成类的实际加载工作的。直接调用 ClassLoader 的 loadClass 方法是另外一种不常用的显式加载类的技术。

 

        ClassLoader 在加载类时有一定的层次关系和规则。在 Java 中,有四种类型的类加载器,分别为:BootStrapClassLoader、ExtClassLoader、AppClassLoader 以及用户自定义的 ClassLoader。这四种类加载器分别负责不同路径的类的加载,并形成了一个类加载的层次结构。

BootStrapClassLoader 处于类加载器层次结构的最高层,负责 sun.boot.class.path 路径下类的加载,默认为 jre/lib 目录下的核心 API 或 -Xbootclasspath 选项指定的 jar 包。ExtClassLoader 的加载路径为 java.ext.dirs,默认为 jre/lib/ext 目录或者 -Djava.ext.dirs 指定目录下的 jar 包加载。AppClassLoader 的加载路径为 java.class.path,默认为环境变量 CLASSPATH 中设定的值。也可以通过 -classpath 选型进行指定。用户自定义 ClassLoader 可以根据用户的需要定制自己的类加载过程,在运行期进行指定类的动态实时加载

 

 

       每个类加载器有自己的名字空间,对于同一个类加载器实例来说,名字相同的类只能存在一个,并且仅加载一次。不管该类有没有变化,下次再需要加载时,它只是从自己的缓存中直接返回已经加载过的类引用。

我们编写的应用类默认情况下都是通过 AppClassLoader 进行加载的。当我们使用 new 关键字或者 Class.forName 来加载类时,所要加载的类都是由调用 new 或者 Class.forName 的类的类加载器(也是 AppClassLoader)进行加载的。要想实现 Java 类的热替换,首先必须要实现系统中同名类的不同版本实例的共存,通过上面的介绍我们知道,要想实现同一个类的不同版本的共存,我们必须要通过不同的类加载器来加载该类的不同版本。另外,为了能够绕过 Java 类的既定加载过程,我们需要实现自己的类加载器,并在其中对类的加载过程进行完全的控制和管理。

 

编写自定义的 ClassLoader

为了能够完全掌控类的加载过程,我们的定制类加载器需要直接从 ClassLoader 继承。首先我们来介绍一下 ClassLoader 类中和热替换有关的的一些重要方法。

  • findLoadedClass: 每个类加载器都维护有自己的一份已加载类名字空间,其中不能出现两个同名的类。凡是通过该类加载器加载的类,无论是直接的还是间接的,都保存在自己的名字空间中,该方法就是在该名字空间中寻找指定的类是否已存在,如果存在就返回给类的引用,否则就返回 null。这里的直接是指,存在于该类加载器的加载路径上并由该加载器完成加载,间接是指,由该类加载器把类的加载工作委托给其他类加载器完成类的实际加载。
  • getSystemClassLoader Java2 中新增的方法。该方法返回系统使用的 ClassLoader。可以在自己定制的类加载器中通过该方法把一部分工作转交给系统类加载器去处理。
  • defineClass: 该方法是 ClassLoader 中非常重要的一个方法,它接收以字节数组表示的类字节码,并把它转换成 Class 实例,该方法转换一个类的同时,会先要求装载该类的父类以及实现的接口类。
  • loadClass: 加载类的入口方法,调用该方法完成类的显式加载。通过对该方法的重新实现,我们可以完全控制和管理类的加载过程。
  • resolveClass: 链接一个指定的类。这是一个在某些情况下确保类可用的必要方法,详见 Java 语言规范中“执行”一章对该方法的描述。

了解了上面的这些方法,下面我们来实现一个定制的类加载器来完成这样的加载流程:我们为该类加载器指定一些必须由该类加载器直接加载的类集合,在该类加载器进行类的加载时,如果要加载的类属于必须由该类加载器加载的集合,那么就由它直接来完成类的加载,否则就把类加载的工作委托给系统的类加载器完成。

 

package com.test.java.classload;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;

public class HotswapCL extends ClassLoader{
	private String basedir; // 需要该类加载器直接加载的类文件的基目录
    private HashSet dynaclazns; // 需要由该类加载器直接加载的类名

    public HotswapCL(String basedir, String[] clazns) throws IOException { 
        super(null); // 指定父类加载器为 null 
        this.basedir = basedir; 
        dynaclazns = new HashSet(); 
        loadClassByMe(clazns); 
    } 

    private void loadClassByMe(String[] clazns) throws IOException { 
        for (int i = 0; i < clazns.length; i++) { 
            loadDirectly(clazns[i]); 
            dynaclazns.add(clazns[i]); 
        } 
    } 

    private Class loadDirectly(String name) throws IOException { 
        Class cls = null; 
        StringBuffer sb = new StringBuffer(basedir); 
        String classname = name.replace('.', File.separatorChar) + ".class";
        sb.append(File.separator + classname); 
        File classF = new File(sb.toString()); 
        cls = instantiateClass(name,new FileInputStream(classF),
            classF.length()); 
        return cls; 
    }   		

    private Class instantiateClass(String name,InputStream fin,long len) throws IOException{ 
        byte[] raw = new byte[(int) len]; 
        fin.read(raw); 
        fin.close(); 
        return defineClass(name,raw,0,raw.length); 
    } 
    
	protected Class loadClass(String name, boolean resolve) 
            throws ClassNotFoundException { 
        Class cls = null; 
        cls = findLoadedClass(name); 
        if(!this.dynaclazns.contains(name) && cls == null) 
            cls = getSystemClassLoader().loadClass(name); 
        if (cls == null) 
            throw new ClassNotFoundException(name); 
        if (resolve) 
            resolveClass(cls); 
        return cls; 
    } 

} 


 待替换的示例类

public class Foo  {

	public void sayHello() {
//		System.out.println("hello world! (version one)");
		System.out.println("hello world! (version two)");

	}

}

 在当前工作目录下建立一个新的目录 swap,把编译好的 Foo.class 文件放在该目录中。接下来要使用我们前面编写的 HotswapCL 来实现该类的热替换

package com.test.java.classload;

import java.lang.reflect.Method;

public class TestMain {

	/**
	 * @param args
	 */
	@SuppressWarnings({ "unchecked", "static-access" })
	public static void main(String[] args) {
		HotswapCL cl;
		String userDir = System.getProperty("user.dir");
		int i = 0;
		try {
			while (i < 100) {
				i++;
				cl = new HotswapCL(userDir + "\\swap", new String[] { "Foo" });
				Class cls = cl.loadClass("Foo");
				Object foo = cls.newInstance();
				
				Method m = foo.getClass().getMethod("sayHello", new Class[] {});
				m.invoke(foo, new Object[] {});
				Thread.currentThread().sleep(1000);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
 

在线升级系统的设计原则

在上小节中,我们给出了一个 Java 类热替换的实例,掌握了这项技术,就具备了实现在线升级系统的基础。但是,对于一个真正的产品系统来说,升级本省就是一项非常复杂的工程,如果要在线升级,就会更加复杂。其中,实现类的热替换只是最后一步操作,在线升级的要求会对系统的整体设计带来深远的影响。下面我们来谈谈在线升级系统设计方面的一些原则:

  • 在系统设计一开始,就要考虑系统的哪些部分是需要以后在线升级的,哪些部分是稳定的。

    虽然我们可以把系统设计成任何一部分都是可以在线升级的,但是其成本是非常高昂的,也没有必要。因此,明确地界定出系统以后需要在线升级的部分是明智之举。这些部分常常是系统业务逻辑规则、算法等等。

  • 设计出规范一致的系统状态转换方法。

    替换一个类仅仅是在线升级系统所要做的工作中的一个步骤,为了使系统能够在升级后正常运行,就必须保持升级前后系统状态的一致性。因此,在设计时要考虑需要在线升级的部分所涉及的系统状态有哪些,把这些状态设计成便于获取、设置和转换的,并用一致的方式来进行。

  • 明确出系统的升级控制协议。

    这个原则是关于系统在线升级的时机和流程控制的,不考虑系统的当前运行状态就贸然进行升级是一项非常危险的活动。因此在系统设计中, 就要考虑并预留出系统在线升级的控制点, 并定义清晰、明确的升级协议来协调、控制多个升级实体的升级次序,以确保系统在升级的任何时刻都处在一个确定的状态下。

  • 考虑到升级失败时的回退机制。

    即使我们做了非常缜密细致的设计,还是难以从根本上保证系统升级一定是成功的,对于大型分布式系统来说尤其如此。因此在系统设计时,要考虑升级失败后的回退机制。

分享到:
评论

相关推荐

    Java类热替换(热部署)源码

    在Java开发过程中,为了提高开发效率和调试便捷性,我们经常需要实现类的热替换(Hot Deployment),也就是在不重启应用的情况下更新或替换已加载的类。Java类热替换技术通常涉及类加载器(ClassLoader)的自定义...

    java简单热替换

    Java热替换(Hot Swap)是一种在程序运行时更新或替换类的方法实现,而无需重启应用的技术。这个特性在开发环境中非常有用,因为它可以显著提高开发效率,减少因反复重启应用而导致的时间消耗。Java的热替换通常与...

    java实现class的热替换

    java实现class的热替换

    springboot+java类热加载

    本文将深入探讨"springboot+java类热加载"这一主题,包括其核心概念、实现方式以及相关技术。 **热加载(Hot Loading)**是开发过程中的一个重要功能,它允许开发者在不重启应用的情况下更新代码,提高开发效率。在...

    Java在运行时无限制地重新定义类

    其中,DCEVM是一个可替代原生JVM的扩展,它允许Java程序在运行时替换类的字节码。JRebel则是一个商业的开发工具,它可以与多种IDE(如IntelliJ IDEA和Eclipse)集成,提供快速的代码更新功能。 在项目...

    java项目实现热更源码-javaagent

    4. **代码热替换**:当新的代码版本准备就绪时,我们可以将新版本的字节码注入到Agent,然后使用Instrumentation的`retransformClasses`方法来替换旧的类定义。需要注意的是,热更新过程中要确保类的兼容性,避免因...

    支持Java热部署的插件

    Java热部署主要涉及到JVM(Java虚拟机)和类加载器(ClassLoader)的工作机制。JVM在运行时会加载类文件到内存中,当类被修改后,热部署插件能够检测到这种变化,并替换内存中的旧版本类,而不需要停止服务。这个...

    java类重载,可以用于热更新

    但有一种称为"热部署"(Hot Deployment)或"热替换"(Hot Swap)的技术,可以在不重启应用的情况下替换已加载的类。这通常在开发环境中使用,以提高调试效率。JVM通过一些工具和插件,如JRebel、JBoss ClassLoader、...

    java热加载Class文件

    JRebel是一款商业工具,它通过插桩技术实现在运行时动态替换类。DCEVM则是一种可替代原生JVM的版本,它支持删除、替换和添加类,甚至可以在运行时增加方法和字段。 实现Java热加载通常涉及以下步骤: 1. **类加载...

    JAVA类加载器分析--热部署的缺陷(有代码示例,及分析)

    热部署的基本思想是当源代码发生变化时,能够即时替换已加载的类,而无需重启应用。Java提供了一些工具和框架,如JRebel、DCEVM等,来实现这一目标。然而,热部署并不是没有缺陷的。其中一个问题在于类加载器的双亲...

    java热部署工具

    1. **JRebel**: JRebel是一款商业的Java热部署工具,它可以直接在JVM中实现类的热更新,而无需重启服务器。开发者只需要在IDE中配置JRebel插件,并将项目指向JRebel代理的类加载器,就可以实时看到代码更改的效果。 ...

    java class reload

    Java 类的热重载(Hot Deployment)或热替换(Hot Swap)是开发过程中非常实用的功能,它允许开发者在不重启应用程序服务器(如Tomcat)的情况下更新已部署的类,从而提高开发效率。在这个例子中,我们将探讨如何在...

    Spring bean 动态注册,jar包热替换

    可以实现jar的热替换。spring的bean动态加载则需要对相应的bean进行动态注册,以及jar与class文件动态加载。测试示例中是spring boot 的部分代码,动态加载的内容为接口实现类,且初始化时加载本地的实现类,动态...

    java热加载Class文件.zip

    Java热加载Class文件技术是一种在不重启应用服务器的情况下更新或替换正在运行的Java类的方法,这对于开发者来说是一项非常实用的功能,因为它极大地提高了开发效率。在传统的开发过程中,修改代码后通常需要停止、...

    自定义Java类加载器

    - **热部署**:在开发环境中,可以实现快速替换和更新类,而无需重启应用程序。 - **模块化系统**:不同模块使用不同的类加载器,隔离不同模块间的类,避免冲突。 - **安全沙箱**:每个加载器都有自己的命名空间,...

    Java类重新加载101对象类和类加载器Java开发Jav

    当我们谈论“Java类重新加载101”时,我们实际上是在讨论如何在不重启应用程序的情况下,更新或替换已加载的类。这对于开发环境中的调试和测试尤其有用,因为它允许开发者快速迭代代码并查看结果,而无需频繁地重启...

    实现Java热部署的几种解决方案.docx

    Java 热部署是指在不停止应用程序的情况下,动态地更新和替换应用程序中的类、方法或资源。这种技术可以提高开发效率、缩短开发周期、降低维护成本。今天,我们将讨论实现 Java 热部署的几种解决方案。 1. Spring-...

    java 类加载器 class loader

    自定义类加载器是Java平台灵活性的一个体现,它可以用来实现动态加载、热部署等功能。创建自定义类加载器通常需要继承`java.lang.ClassLoader`,重写`findClass()`或`loadClass()`方法,从而控制类的查找和加载过程...

    Java加载。jar包

    通过对 Java 类加载机制的深入了解,我们可以更好地控制类的加载过程,这对于开发高级应用、热部署、动态加载等功能非常有用。自定义 `ClassLoader` 是实现这些功能的基础,而掌握 `ClassLoader` 的核心方法则是关键...

    Java ClassLoader学习总结

    热替换是指在不停止 JVM 的情况下,动态地加载和替换类定义。通过自定义的 ClassLoader,我们可以实现类的热替换,例如,在 Web 服务器中,我们可以使用自定义的 ClassLoader 来实现热替换,以提高 Web 服务器的性能...

Global site tag (gtag.js) - Google Analytics