我正在研究一个监视目录的程序,并在目录中看到更改时运行目录中的所有测试。
这要求程序动态加载类,而不是获取缓存的副本。
我可以动态加载测试类。在运行时检测并使用对测试的更改。但是,测试所测试的类不是这种情况。
我的代码用于动态加载类并返回测试类列表:
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;
将DynamicClassLoader作为此处所述的重新加载器How to force Java to reload class upon instantiation?
知道怎么解决吗?我以为所有类都会动态加载。但请注意,我不会在DynamicClassLoader中覆盖loadclass,因为如果我执行测试类,则给出init
编辑: 这不起作用,类被加载但是其中的测试未被检测到......
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();
}
编辑编辑:我检测到这样的测试方法:
for(Method method: dynamicClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
requests.add(Request.method(dynamicClass, method.getName()));
}
}
答案 0 :(得分:9)
如果您使用与链接答案完全相同的自定义ClassLoader
,则不会覆盖方法protected Class<?> loadClass(String name, boolean resolve)
。这意味着当JVM解析依赖关系时,它仍将委托给父类加载器。当然,当它没有委托给父母ClassLoader
时,它有可能错过一些必需的课程。
最简单的解决方案是设置正确的父类加载器。您目前正在传递Thread.currentThread().getContextClassLoader()
,这有点奇怪,因为您的主要目的是委派不委托给该加载程序但加载更改的类。您必须考虑哪些类加载器存在以及使用哪些类加载器。例如。如果类Foo
在您当前代码的范围内,但您希望(重新)使用新的ClassLoader加载它,则Foo.class.getClassLoader().getParent()
将成为新ClassLoader
的正确委托父级。请注意,它可能是null
,但这并不重要,因为在这种情况下,它将使用引导加载程序,这是正确的父项。
请注意,当您设置与您的意图相匹配的右父ClassLoader
时,您不再需要该自定义ClassLoader
。默认实现(参见URLClassLoader
)已经做了正确的事情。对于当前的Java版本,Closeable
使其更适合动态加载方案。
以下是类重新加载的简单示例:
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));
}
}
}