如何强制 Java 在实例化时重新加载类?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3971534/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to force Java to reload class upon instantiation?
提问by nat101
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.
背景:
我有一个 MainTest 类,它有许多按钮,每个按钮实例化一个我正在编码/测试的类。我希望这些类的代码/测试周期很快,并快速查看更改的效果,一分钟几次。稳定的 MainTest 需要大约 20 秒才能加载,如果我不需要为它实例化的类中的每个更改重新加载它,这将不是问题。我想加载 MainTest 一次,当它实例化另一个类时,我们称之为 ChildTest,多次(根据按钮事件),它应该重新加载最新版本的 ChildTest。
简而言之,问题是:
您如何告诉 java 'new' 命令从磁盘而不是从 jvm 缓存重新加载类?
我试过 Class.ForName 但它没有任何区别。
我也尝试过使用自定义类加载器(从开源复制),但无济于事。
回答by maerics
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.
没有希望“重载”new
运算符,但您当然可以编写一个自定义类加载器,每次您要求它加载类时,只需重新加载字节码。没有开箱即用的类加载器可以满足您的需求,因为它们都假设类定义在 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).
但这是你实现它的方式。创建一个名为的类加载器,例如,Reloader
它会覆盖方法loadClass
和findClass
方法,以便它们在每次被调用时简单地从磁盘重新加载类文件(而不是“缓存”它们以备后用)。然后您只需要在new Reloader().loadClass("foo.bar.MyClassName")
您怀疑类定义已更改时调用(例如,作为测试框架生命周期方法的一部分)。
This articlefills 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. 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.
在 main 方法中每次调用“do/while”块时,都会实例化一个新的 Reloader,它从磁盘加载类并将其返回给调用者。因此,如果您bin/MyClass.class
使用不同的重载toString
方法覆盖文件以包含新的实现,那么您应该每次都看到新的实现。
回答by Peter Lawrey
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. http://download-llnw.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html
编辑:除了使用调试 API,您还可以使用 Instrumentation。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)
或者您可以通过为内存提供转储和重新加载功能来更快地加载应用程序。通过这种方式,您可以从快照(可能立即)启动您的应用程序
回答by Valchris
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 以确定再次加载它是否会覆盖以前的版本。
回答by nat101
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. will try it later.
但是,maerics 的答案看起来也不错。稍后会尝试。
回答by andcoz
You can give a try to 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.
你可以试试Java Rebel。它是一个“仅限开发”的类加载器,旨在完全满足您的需求,但是,对于您的需求来说,它可能会很昂贵。在您的情况下,Peter Lawrey 的解决方案可能就足够了。
回答by Alan Geleynse
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. It will then recompile that class and reload it.
如果您在支持此功能的 IDE 中运行,则每次修改类时只需单击该按钮即可。然后它将重新编译该类并重新加载它。