在运行时重新加载使用过的类 Java
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20091075/
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
Reload used classes at runtime Java
提问by Sven
I'm working on a program that watches a directory and runs all tests in the directory when it sees changes in the directory.
我正在开发一个程序,该程序监视目录并在看到目录中的更改时运行该目录中的所有测试。
This requires the program to dynamically load the classes, instead of getting the cached copies.
这需要程序动态加载类,而不是获取缓存的副本。
I can dynamically load the test classes. Changes to the tests get detected and used at runtime. However, this isn't the case for the classes tested by the tests.
我可以动态加载测试类。在运行时检测并使用对测试的更改。但是,对于测试所测试的类而言,情况并非如此。
My code for dynamically loading the classes and returning a list of test classes:
我用于动态加载类并返回测试类列表的代码:
List<Class<?>> classes = new ArrayList<Class<?>>();
for (File file : classFiles) {
String fullName = file.getPath();
String name = fullName.substring(fullName.indexOf("bin")+4)
.replace('/', '.')
.replace('\', '.');
name = name.substring(0, name.length() - 6);
tempClass = new DynamicClassLoader(Thread.currentThread().getContextClassLoader()).findClass(name) } catch (ClassNotFoundException e1) {
// TODO Decide how to handle exception
e1.printStackTrace();
}
boolean cHasTestMethods = false;
for(Method method: tempClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
cHasTestMethods = true;
break;
}
}
if (!Modifier.isAbstract(cachedClass.getModifiers()) && cHasTestMethods) {
classes.add(tempClass);
}
}
return classes;
with DynamicClassLoader being as the Reloader described here How to force Java to reload class upon instantiation?
DynamicClassLoader 作为此处描述的 Reloader如何强制 Java 在实例化时重新加载类?
Any idea how to fix it? I thought all classes would be dynamically loaded. Note however that I don't overwrite loadclass in my DynamicClassLoader because if I do my test classes give init
知道如何修复它吗?我认为所有的类都会被动态加载。但是请注意,我不会在我的 DynamicClassLoader 中覆盖 loadclass 因为如果我做我的测试类给 init
EDIT: This doesn't work, the class gets loaded but the tests in it aren't detected...
编辑:这不起作用,该类已加载但未检测到其中的测试...
List<Request> requests = new ArrayList<Request>();
for (File file : classFiles) {
String fullName = file.getPath();
String name = fullName.substring(fullName.indexOf("bin")+4)
.replace('/', '.')
.replace('\', '.');
name = name.substring(0, name.length() - 6);
Class<?> cachedClass = null;
Class<?> dynamicClass = null;
try {
cachedClass = Class.forName(name);
URL[] urls={ cachedClass.getProtectionDomain().getCodeSource().getLocation() };
ClassLoader delegateParent = cachedClass .getClassLoader().getParent();
URLClassLoader cl = new URLClassLoader(urls, delegateParent) ;
dynamicClass = cl.loadClass(name);
System.out.println(dynamicClass);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Edit edit: i detect the test methods like this:
编辑编辑:我检测这样的测试方法:
for(Method method: dynamicClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
requests.add(Request.method(dynamicClass, method.getName()));
}
}
回答by Holger
If you used the custom ClassLoader
exactly like in the linked answer it is not overriding the method protected Class<?> loadClass(String name, boolean resolve)
. This implies that when the JVM is resolving dependencies it will still delegate to the parent class loader. And, of course, when it was not delegating to the parent ClassLoader
it had the risk of missing some required classes.
如果您ClassLoader
在链接的答案中完全使用自定义,则不会覆盖方法protected Class<?> loadClass(String name, boolean resolve)
。这意味着当 JVM 解析依赖项时,它仍然会委托给父类加载器。而且,当然,当它没有委托给父级时,ClassLoader
它就有可能错过一些必需的课程。
The easiest solution is to set up the right parent class loader. You are currently passing Thread.currentThread().getContextClassLoader()
which is a bit strange as your main intention is that the delegation should notdelegate to that loader but load the changed classes. You have to think about which class loaders exist and which to use and which not. E.g. if the class Foo
is within the scope of your current code but you want to (re)load it with the new ClassLoader, Foo.class.getClassLoader().getParent()
would be the right delegate parent for the new ClassLoader
. Note that it might be null
but this doesn't matter as in this case it would use the bootstrap loader which is the correct parent then.
最简单的解决方案是设置正确的父类加载器。您正在传递Thread.currentThread().getContextClassLoader()
这是一个有点奇怪作为你的主要目的是,代表团应该不会委托给该加载器加载,但在改变类。您必须考虑哪些类加载器存在,哪些可以使用,哪些不可以。例如,如果该类Foo
在您当前代码的范围内,但您想(重新)使用新的 ClassLoader 加载它,Foo.class.getClassLoader().getParent()
则将是新的ClassLoader
. 请注意,它可能是,null
但这并不重要,因为在这种情况下它会使用引导加载程序,这是正确的父级。
Note that when you set up the right parent ClassLoader
matching your intentions you don't need that custom ClassLoader
anymore. The default implementation (see URLClassLoader
) already does the right thing. And with current Java versions it is Closeable
making it even more suitable for dynamic loading scenarios.
请注意,当您设置ClassLoader
符合您意图的正确父级时,您不再需要该自定义ClassLoader
。默认实现(请参阅 参考资料URLClassLoader
)已经做了正确的事情。并且在当前的 Java 版本中,它Closeable
使其更适合动态加载场景。
Here is a simple example of a class reloading:
这是一个类重新加载的简单示例:
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
public class ReloadMyClass
{
public static void main(String[] args)
throws ClassNotFoundException, IOException {
Class<?> myClass=ReloadMyClass.class;
System.out.printf("my class is Class@%x%n", myClass.hashCode());
System.out.println("reloading");
URL[] urls={ myClass.getProtectionDomain().getCodeSource().getLocation() };
ClassLoader delegateParent = myClass.getClassLoader().getParent();
try(URLClassLoader cl=new URLClassLoader(urls, delegateParent)) {
Class<?> reloaded=cl.loadClass(myClass.getName());
System.out.printf("reloaded my class: Class@%x%n", reloaded.hashCode());
System.out.println("Different classes: "+(myClass!=reloaded));
}
}
}