我知道我缺少一些基本知识。我试图动态编译一个类,然后在更改后重新加载它。以下代码有效。但是,当我两次调用它(更改.java之后一次)时,类定义不会更新。我想念什么?

File file = new File("/eraseme/Eraseme.java");
File[] files = new File[] {file};
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(files);
CompilationTask task = compiler.getTask(null, fileManager, null, null,null,compilationUnits);
boolean madeIt = task.call(); // works

File classFile = new File("/eraseme/Eraseme.class");
URL url = classFile.toURL();
URL[] urls = new URL[] { url };
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("Eraseme");
TestApi test = (TestApi) cls.newInstance();
System.out.println(test.getVersion());

最佳答案

您所缺少的是传递给URLClassLoader的url应该是随后被搜索的目录。发生的情况是,您正在构造的URLClassLoader实际上没有找到.class文件,但是找不到该类,并且退回到让父类加载器加载该类。

具体来说,尝试将代码的第二位修改为:

File classFile = new File("/eraseme");
URL url = classFile.toURL();
URL[] urls = new URL[] { url };
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("Eraseme");  // will try to load /eraseme/Eraseme.class
TestApi test = (TestApi) cls.newInstance();
System.out.println(test.getVersion());


请注意,如果您的类(与包一起)被称为com.mycompany.erasestuff.EraseMe,则类加载器将在文件/eraseme/com/mycompany/erasestuff/EraseMe.class中查找

请注意,执行此操作时,您不希望系统类加载器能够加载EraseMe类。系统类加载器将具有优先权,您的URLClassloader将不会加载任何内容。 (不幸的是,当您对类加载器说“加载该类”时,它首先检查其父类加载器是否可以加载该类,然后尝试自行加载它。如果系统类加载器可以加载该类,您将永远不会获得其他版本)

08-18 10:30