简体   繁体   English

如何强制Java在实例化时重新加载类?

[英]How to force Java to reload class upon instantiation?

Background: 背景:
I have a MainTest class that has many buttons, each of which instantiate a class that I am coding/testing. 我有一个MainTest类,它有许多按钮,每个按钮都实例化我正在编码/测试的类。 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. 稳定的MainTest大约需要20秒才能加载,如果我不需要为它实例化的类中的每次更改重新加载它,这不会成为问题。 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. 我想加载一次MainTest,当它实例化另一个类时,让我们多次调用它ChildTest(在按钮事件时),它应该重新加载最新版本的ChildTest。

The question in short: 问题简而言之:
How do you tell the java 'new' command to reload the class from disk and not from jvm cache? 你怎么告诉java'new'命令从磁盘而不是从jvm缓存重新加载类?

I tried Class.ForName but it didn't make a difference. 我尝试过Class.ForName,但它并没有什么区别。
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. 没有希望“重载” new运算符,但你可以编写一个自定义的类加载器,只需每次要求它加载一个类时重新加载字节码。 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. 没有开箱即用的类加载器可以完成你正在寻找的东西,因为它们都假设类定义不会在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). 创建一个名为Reloader的类加载器,它重写方法loadClassfindClass方法,这样它们只需在每次调用时从磁盘重新加载类文件(而不是“缓存”它们以供以后使用)。 Then you just have to call new Reloader().loadClass("foo.bar.MyClassName") any time you suspect the class definition has changed (eg as part of your testing framework's lifecycle methods). 然后,只要您怀疑类定义已更改(例如,作为测试框架的生命周期方法的一部分),您就必须调用new Reloader().loadClass("foo.bar.MyClassName") )。

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: 这是一个简单的工作示例,它重复加载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("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. 在main方法中每次调用“do / while”块时,都会实例化一个新的Reloader,它从磁盘加载该类并将其返回给调用者。 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. 因此,如果您覆盖bin/MyClass.class文件以包含具有不同的重载toString方法的新实现,那么您应该每次都看到新的实现。

It sounds like you want ot use the hot deployment when can be used with the debugger. 听起来您可能希望在与调试器一起使用时使用热部署。 When you debug a problem and recomiple some of its classes you can get the option to reload the changed classes. 当您调试问题并重新安装其某些类时,您可以选择重新加载更改的类。

EDIT: Apart from using the debugging API, you can use Instrumentation. 编辑:除了使用调试API,您可以使用Instrumentation。 http://download-llnw.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html http://download-llnw.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html

However, since using a debugger is by far the simplest way to do this, if this doesn't work, you are likely to run into the same problems. 但是,由于使用调试器是迄今为止最简单的方法,如果这不起作用,您可能会遇到同样的问题。

It sounds like what you need to test smaller pieces of work so it takes less than a second run some subset of your application. 这听起来像你需要测试较小的工作,所以它需要不到一秒的时间运行你的应用程序的一些子集。

Or you could load your application faster by providing a dump and re-load facility for the memory. 或者,您可以通过为内存提供转储和重新加载工具来加快应用程序的运行速度。 This way you could start your application from a snapshot (perhaps immediately) 这样您就可以从快照启动应用程序(可能是立即启动)

Sounds a little scary, but this should help. 听起来有点可怕,但这应该会有所帮助。

http://download.oracle.com/javase/1.4.2/docs/api/java/lang/ClassLoader.html http://download.oracle.com/javase/1.4.2/docs/api/java/lang/ClassLoader.html

ClassLoader can dynamically load classes at runtime, I would read the api to determine if loading it again overrides the previous version. ClassLoader可以在运行时动态加载类,我会读取api以确定是否再次加载它会覆盖以前的版本。

I found an article on exactly this problem. 我发现了一篇关于这个问题的文章。
http://www.zeroturnaround.com/blog/reloading-objects-classes-classloaders/ http://www.zeroturnaround.com/blog/reloading-objects-classes-classloaders/

BUT, maerics answer looks good too. 但是,maerics答案看起来也很好。 will try it later. 稍后会试试。

You can give a try to Java Rebel . 您可以尝试Java Rebel It is a "development-only" class-loader designed to do exactly what you need but, perhaps, it could be expensive for your needs. 它是一个“仅开发”的类加载器,旨在完全满足您的需求,但也许,它可能对您的需求来说很昂贵。 In your case, Peter Lawrey's solution could be enough. 在你的情况下,Peter Lawrey的解决方案就足够了。

I am not sure how to do this in code, but NetBeans does have an "Apply Code Changes" button that will do this. 我不知道如何在代码中执行此操作,但NetBeans确实有一个“应用代码更改”按钮来执行此操作。

However, it can only change the implementation of the class, it cannot change it's signature. 但是,它只能改变类的实现,它不能改变它的签名。 This means you cannot add, remove, or change instance variables or methods. 这意味着您无法添加,删除或更改实例变量或方法。 This is due to the design of the JVM which does not allow these to be changed once a class has been loaded once. 这是由于JVM的设计,一旦加载一次类,它就不允许更改它们。

Since NetBeans can do it, there must be a way, but I do not know how to do it manually. 由于NetBeans可以做到这一点,必须有办法,但我不知道如何手动完成。

If you run in an IDE that supports this feature, you can just click the button each time you modify a class. 如果您在支持此功能的IDE中运行,则只需在每次修改类时单击该按钮。 It will then recompile that class and reload it. 然后它将重新编译该类并重新加载它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM