如何强制Java在实例化时重新加载类? [英] How to force Java to reload class upon instantiation?

查看:152
本文介绍了如何强制Java在实例化时重新加载类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:

我有一个MainTest类,它有许多按钮,每个都实例化一个类,我编码/测试。我想让这些类的代码/测试周期很快,并快速看到我的更改的效果,每分钟几次。 MainTest是稳定的,需要大约20秒来加载,这不会是一个问题,如果我不需要重新加载它为它实例化的类中的每个更改。我想加载MainTest一次,当它实例化另一个类,我们称之为ChildTest,多次(按钮事件),它应该重新加载最新版本的ChildTest。


问题:

如何告诉java'new'命令从磁盘重新载入类,而不是从jvm缓存中重载类?




我试过Class.ForName,但它没有什么区别。

我也试过使用自定义类加载器(从开源复制),无效。


<没有希望重载运算符,但你可以写一个自定义类加载器,简单地每次你请求它加载类时重新加载字节码。没有现成的类加载器将会做你想要的,因为他们都假设类定义在JVM的生命周期中不会改变。



但这里是你如何使它发生。创建一个称为 LoadClass findClass 的方法的类加载器 Reloader code>方法,以便他们只需重新加载类文件从磁盘每次调用(而不是缓存他们供以后使用)。然后你只要在你怀疑类定义已经改变的时候调用 new Reloader()。loadClass(foo.bar.MyClassName)框架的生命周期方法)。



本文填充一些细节,但是缺少一些重要的点,特别是关于使用类加载器的新实例用于随后的重新加载,并在适当时委托给默认的类加载器。这里有一个简单的工作示例,它重复加载类 MyClass ,并假定其类文件存在于相对的./bin目录中:

  public class Reloader extends ClassLoader {
public static void main(String [] args)throws Exception {
do {
Object foo = new Reloader()。loadClass(MyFoo)。newInstance();
System.out.println(LOADED:+ foo); // Overload MyFoo#toString()for effect
System.out.println(当MyFoo.class发生改变时,按Enter>
System.in.read();
} while(true);
}

@Override
public Class<?> loadClass(String s){
return findClass(s​​);
}

@Override
public Class<?> findClass(String s){
try {
byte [] bytes = loadClassData(s);
return defineClass(s​​,bytes,0,bytes.length);
} catch(IOException ioe){
try {
return super.loadClass(s​​);
} catch(ClassNotFoundException ignore){}
ioe.printStackTrace(System.out);
return null;
}
}

private byte [] loadClassData(String className)throws IOException {
File f = new File(bin /+ className.replaceAll \\。,/)+.class);
int size =(int)f.length();
byte buff [] = new byte [size];
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
dis.readFully(buff);
dis.close();
return buff;
}
}

在每次调用do / while在main方法中,实例化了一个新的Reloader,它从磁盘加载类并将其返回给调用者。因此,如果您覆盖 bin / MyClass.class 文件以包含使用不同的重载 toString 方法的新实现,那么你每次都应该看到新的实现。


Background:
I have a MainTest class that has many buttons, each of which instantiate a class that I am coding/testing. I want the code/test cycle for these classes to be quick, and see the effect of my changes quickly, a few times a minute. MainTest which is stable takes about 20 seconds to load, which would not be a problem had I not needed to reload it for each change in the classes it instantiates. I want to load MainTest once, and when it instantiates another class, let's call it ChildTest, numerous times (upon button event), it should reload the latest version of ChildTest.

The question in short:
How do you tell the java 'new' command to reload the class from disk and not from jvm cache?


I tried Class.ForName but it didn't make a difference.
I have also tried using a custom classloader (copied from open source), to no avail.

解决方案

There's no hope of "overloading" the new operator but you could certainly write a custom class loader that simply reloads the bytecode every time you ask it to load a class. No out-of-the-box classloaders will do what you're looking for because they all assume that the class definition will not change through the life of the JVM.

But here's how you make it happen. Create a class loader called, say, Reloader which overrides the methods loadClass and findClass methods so that they simply reload the class files from disk every time they are called (instead of "caching" them for later use). Then you just have to call new Reloader().loadClass("foo.bar.MyClassName") any time you suspect the class definition has changed (e.g. as part of your testing framework's lifecycle methods).

This article fills in some of the details but misses some important points, especially about using new instances of the classloader for subsequent reloads and delegating to the default classloader when appropriate. Here is a simple working example which repeatedly loads the class MyClass and assumes its class file exists in the relative "./bin" directory:

public class Reloader extends ClassLoader {
    public static void main(String[] args) throws Exception {
        do {
            Object foo = new Reloader().loadClass("MyFoo").newInstance();
            System.out.println("LOADED: " + foo); // Overload MyFoo#toString() for effect
            System.out.println("Press <ENTER> when MyFoo.class has changed");
            System.in.read();
        } while (true);
    }

    @Override
    public Class<?> loadClass(String s) {
        return findClass(s);
    }

    @Override
    public Class<?> findClass(String s) {
        try {
            byte[] bytes = loadClassData(s);
            return defineClass(s, bytes, 0, bytes.length);
        } catch (IOException ioe) {
            try {
                return super.loadClass(s);
            } catch (ClassNotFoundException ignore) { }
            ioe.printStackTrace(System.out);
            return null;
        }
    }

    private byte[] loadClassData(String className) throws IOException {
        File f = new File("bin/" + className.replaceAll("\\.", "/") + ".class");
        int size = (int) f.length();
        byte buff[] = new byte[size];
        FileInputStream fis = new FileInputStream(f);
        DataInputStream dis = new DataInputStream(fis);
        dis.readFully(buff);
        dis.close();
        return buff;
    }
}

At each invocation of the "do/while" block in the main method, a new Reloader is instantiated which loads the class from disk and returns it to the caller. So if you overwrite the bin/MyClass.class file to contain a new implementation with a different, overloaded toString method, then you should see the new implementation each time.

这篇关于如何强制Java在实例化时重新加载类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆