去年有一款产品“jrebel”可以实现运行时的类重新加载(包括方法的新增和删除),大家都知道JVM使用Application ClassLoader来加载放在classpath下的类,而且只加载一次下次在访问就会从缓存中取,所以要实现类重载只能是重新创建一个新的ClassLoader在来加载变化后的class。
问题:
1、JVM是在什么时候创建类的ClassLoader
2、创建自己的ClassLoader没什么问题,但是创建后的ClassLoader如何注册到JVM中呢?不知道有没有JVM的高手可以解答这个问题注意:此贴并是不讨论如何创建自定义的ClassLoader,而是需要一个JVM的钩子能够实现的需要时自动创建ClassLoader来达到类的重新加载的功能。

解决方案 »

  1.   

    帮你顶下吧,还真的要JVM比较熟悉才能回答你
      

  2.   

    这个可能和一些application server中每个application都有各自classloader相似。那些server实现了自己的classloader但是也没有在JVM里面注册。每次reload application的时候程序控制了自己定义的classloader重新加载的动作。我觉得这种reload(或者hot deployment)机制可能和您想要的效果差不多。
      

  3.   

    应用服务器虽然有自定义的classloader但是加载用户写的类的时候还是用的JVM加载器,大家想想平时在NEW的时候调的classloader肯定是java自带的,但是jrebel却可以做到热加载,这个就很奇特了。
      

  4.   

    jvm类加载用的是至上到下的方法,如  baseloader基本-----systemloader---系统
                                             ------extloader扩展  
        你要在用个加载类的话一般是系统加载类的子类,如要它指定是加载一个test类, 当这个test类改变后你想热改变这时你不能用这个类了,应为他以存有test类的信息不会在去加载,你可以在new一个类加载由systemloader做父加裁器。在加载test。返回class 在new出来就成了改变后的类了
      

  5.   

    java.net.URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();.....Class sysclass = URLClassLoader.class;
    try {
    Method method = sysclass.getDeclaredMethod("addURL", new Class[] { URL.class });
    method.setAccessible(true);
    method.invoke(sysLoader, u);
    } catch (Throwable t) {
    t.printStackTrace();
    }
      

  6.   

    chenliuyang可能没看懂问题,此贴并不是要讨论如何写一个自定义加载器来加载摸个类,而是需要实现一个机制能够在开发人员使用new 来创建对象的时候否判断如果此类已经更新则重新加载。没看懂coldanimal代码和此主题的关系,楼上代码利用反射调用URLClassLoader的addURL方法和类重新加载有关系吗?
      

  7.   

    你可以看看jvm内部 的一些脚步开发。可能对你有帮助。自己这几天也想研究下呢
      

  8.   

    http://www.blogjava.net/Unmi/archive/2008/10/22/235823.htmlURL[] externalURLs = new URL[]{new URL("file:../TestHotDeployImpl/bin/")};         
    cl = new URLClassLoader(externalURLs);     
    catClass = cl.loadClass("com.unmi.CatImpl"); 
      

  9.   

    jvm类加载机制还没搞过。。很不明白...
      

  10.   


    // Get the directory (URL) of the reloadable class
    URL[] urls = null;
    try {
        // Convert the file object to a URL
        File dir = new File(System.getProperty("user.dir")
            +File.separator+"dir"+File.separator);
        URL url = dir.toURL();        // file:/c:/almanac1.4/examples/
        urls = new URL[]{url};
    } catch (MalformedURLException e) {
    }try {
        // Create a new class loader with the directory
        ClassLoader cl = new URLClassLoader(urls);    // Load in the class
        Class cls = cl.loadClass("MyReloadableClassImpl");    // Create a new instance of the new class
        myObj = (MyReloadableClass)cls.newInstance();
    } catch (IllegalAccessException e) {
    } catch (InstantiationException e) {
    } catch (ClassNotFoundException e) {
    }
      

  11.   

    感谢coldanimal的回复,我再次说明下需要解决的问题,此贴并不是要讨论如何写一个自定义的ClassLoader,然后通过此ClassLoader主动加载某些类,而是需要一种钩子机制注册进JVM然后在以后使用此JVM进程的所有JAVA代码在new 或已其他形式构建对象的时候能够调用钩子代码判断是否重新加载CLASS。
      

  12.   

    因为jrebel使用代码混淆器,反编译的代码没办法看,目前只知道jrebel在JDK1.5下面是使用javaagent进行代码注册,但是注册了什么没分析出来。
      

  13.   

    if (ClassLoader.getSystemClassLoader() instanceof URLClassLoader)
            u.registerRebelClassLoader((URLClassLoader)ClassLoader.getSystemClassLoader());
      

  14.   

    if(ClassLoader.getSystemClassLoader() instanceof URLClassLoader)
    p.registerRebelClassLoader((URLClassLoader)ClassLoader.getSystemClassLoader());
    //registerRebelClassLoader定义
    public static void registerRebelClassLoader(URLClassLoader urlclassloader)
    {
        if(!REBEL_CLASSLOADER_MAP.containsKey(urlclassloader))
           REBEL_CLASSLOADER_MAP.put(urlclassloader, new do(urlclassloader));
    }
      

  15.   

    类重新加载通常有两种方式。javaagent方式和重载classloader方式
    jrebel的方式是用java -javaagent参数指定的,类加载不是在classloader上做的。
    在java1.5以后,增加了java -javaagent参数,提供了一个动态替换class的方式。
    以jrebel为例:运行参数为java -javaagent jrebel.jar
    在jrebel.jar里的META-INF目录下的MANIFEST.MF文件中定义Premain-Class
    Premain-Class: com.zeroturnaround.javarebel.java5.AgentInstall该class包含一个 public static void premain(String agentArgs, Instrumentation inst)
    的方法作为入口。
    在java.lang.instrument.Instrumentation中提供具体的动态替换功能。
    当然,要做到动态替换class,还要有class更新监控的功能。
    相关知识在API文档中Package java.lang.instrument 及其子目录下有详细的介绍(JDK1.5以后)。
      

  16.   

    没有办法用 new ,应该只能用 静态方法实例化。
    而且一般的加载方式是,在默认classloader里面有一个接口或者父类。
    然后用自定义的加载器加载 要热加载的类。然后把 这个类 实例化转换为 父类或者接口。具体的应该是,有一个静态方法,这个方法返回一个 默认classloader 已经加载了 的接口或父类 的实例。
    然后静态方法首先检查 文件是否已经更新,已经更新就生成一个新的 classloader 加载这个 新的class,然后返回新的实例。如果 默认classloader 里面没有接口或者父类,那就只能返回Object,然后用反射调用具体的方法。这时候用脚本编写比较方便。
      

  17.   

    忘了一点。
    如果不想一个一个的用 静态方法实例化。
    可以 看看 Thread.setContextClassLoader。
    这时候可以用 new.
    不过必须得对 线程控制好。
      

  18.   

    自己构建classloader很多人都可以做,核心问题是你做的classloader如何注册进去,如果说自己写个框架加载自己的类这个目前有很多实现可以参考。比较奇特的是jrebel在使用javaagent注册进去后你程序代码里面不管是用new,还是用反射接口等操作他都能自动检测到并重新加载,这个是我很想知道的。
      

  19.   

    JVM有个插件可以帮助你实现类的重加载~
      

  20.   

    我认为,你可以参考下Tomcat的源码,首先Tomcat编译的时候是通过吧jsp页面动态编译成servlet。然后把编译后的.class文件以文件流的形式传入继承了java.lang.ClassLoader类中重写的defineClass方法中。既然Tomcat可以做到。那么我们就可以节省jsp页面编译成servlet的过程。
    我们通过java中的IO类动态生成.java文件。然后通过Tomcat中的动态编译和加载功能进行加载。没有试过,但是最近在看Tomcat的源码,都是java写的。
    一定可行。
      

  21.   

    http://java.sun.com/javase/6/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html
    http://blog.csdn.net/ThirstyCrow/archive/2008/10/30/3185018.aspx
    可以搜一下这个VirtualMachine,Instrumentation,很强大,可以attach到指定的虚拟机上,指定agent,在agent里面进行实现拦截器,重新加载。jdk自带的tool.jar里面的类。
    现在很多Java框架如Spring都实现有拦截器,不管是基于动态代理还是Asm那样的直接修改字节码。
      

  22.   

    jiewo
    JVM有个插件可以帮助你实现类的重加载~   是什么插件???有没有人研究过jrebel,可以谈谈心得
      

  23.   

    反编译了一下jrebel的代码,里面有两个类TransformNew,AgentInstall
    AgentInstall利用Instrumentation进行拦截,TransformNew把自己new出来的对象就行修改,实际new出来的对象已经是jrebel可以控制的对象了.jrebel用的是bytecode的修改
      

  24.   

    jAgent transform,只是在每个类初次加载进JVM的时候调用一次以后Class就会缓存起来,下次再次new这个对象不会在调用这个方法。这也是苦苦找不到注入点的原因。
      

  25.   

    虽然我对这方面也是一知半解的,但是我也稍微说一下我的看法1.首先要了解,jvm启动时候,会形成三个类加载器,这个前面有人说过了:
    bootstrap classloader , extension classloader , system classloader2.而且要知道,classloader 有一种全盘负责委托机制,也就是说,cl要载入一个class的时候,这个class所依赖的所有class都要由这个classloader载入,除非是你显示的让另一个cl载入。而且委托机制是先让父加载器寻找,要是父加载器找不到的时候,在从自己的类路径中寻找。这样的话,要是想动态载入class的话,是不是可以这样处理:
    比如我们要动态加载一个class,名字为 A ,我们可以构造一个A的父类,叫AParent,然后把AParent放在CLASSPATH下(system classloader加载的类路径),父类由system classloader载入,当我们用自己的classloader动态载入A的时候,发现他有个爹...在载入他之前,jvm会先载入他爹..要由system classloader载入。根据多态,调用AParent方法的时候,其实是调用的A的方法。
    说的可能不对,大家带着批判的眼光来看,期待高手的解答,别让我脑子里面记得东西都是错误的东西。
      

  26.   

    如果能够随意用自己 classloader加载的话就不用这么麻烦了,因为要实现以插件形式类的动态加载就不能限制开发人员只能使用规定的自定义类加载器来加载class,开发人员可以随意的以new、反射灯方式来加创建对象。
      

  27.   

    比较奇特的是jrebel ,一直在强调这个
    感觉楼在推销这个啊
      

  28.   

    jrebel是去年获得jolt生产力大奖的产品,老外开发的本能还没这个技术,不过是对这个产品实现原理很感兴趣提出来大家一起研究下,如若不懂没关系支持顶下就可以了。
      

  29.   

    ClassLoader只有加载类的功能,没有卸载类的功能!而一个ClassLoader对同一个类只能加载一次,除非是不同的ClassLoader(类或实例)。换句话说即便你能做到:把自己定义的ClassLoader注册到了JVM中,你的程序从一开始用的ClassLoader都是你自己定义的,你也无法更新已经Load了的Class!ClassLoader与Class是一个整体,你要想更新Class那你就得创建一个新的ClassLoader实例来Load你更新之后的Class。所以你的思路有点歪,你要做的不是取代系统默认的ClassLoader,而是怎样管理你自己定义的ClassLoaders