Java JavaCompiler.run()也可以编译匿名类
我试图动态加载文本文件并进行编译。
File file = new File("Files/"+fileName+".java");JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, errStream, file.getAbsolutePath());
然后,我稍后将加载已编译的.class文件:
public Class loadStrategyClass(File strategyClassFile) throws IOException {
FileChannel roChannel = new RandomAccessFile(strategyClassFile, "r").getChannel();
ByteBuffer buffer = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, (int)roChannel.size());
return defineClass(strategyClassFile.getName(), buffer, (ProtectionDomain)null);
}
我目前遇到两个问题:首先是我加载的.java文件是否包含匿名类。看来JavaCompiler类不会编译它们。线程“主”中的异常java.lang.IllegalAccessException:类Loader.ClassLoader无法使用修饰符“”访问类Files.myname.myclass
$ 1的成员
第二个:是有时候我会收到NoClassDefFoundError错误:线程“
main”中的异常java.lang.NoClassDefFoundError:Files / myname /
myclass尽管事实上其他类会正确加载并且.class文件在该路径中。
回答:
显然,您loadStrategyClass
是在custom中定义的ClassLoader
。问题在于,defineClass
对于感兴趣的类仅调用一次是不够的,您的类加载器通常必须通过实现才能按需解析类findClass
,以便JVM可以解析依赖项,例如内部类。
您没有指定如何获取方法的strategyClassFile
参数loadStrategyClass
。由于您没有任何选择地运行编译器,因此我想您只是相对于源文件查找了文件。要解决其他依赖关系,需要知道类目录的实际根目录。当您定义存储类文件的位置时,它将变得更加容易,例如
// customize these, if you want, null triggers default behaviorDiagnosticListener<JavaFileObject> diagnosticListener = null;
Locale locale = null;
JavaCompiler c = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm
= c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());
// define where to store compiled class files - use a temporary directory
Path binaryDirectory = Files.createTempDirectory("compile-test");
fm.setLocation(StandardLocation.CLASS_OUTPUT,
Collections.singleton(binaryDirectory.toFile()));
JavaCompiler.CompilationTask task = c.getTask(null, fm,
diagnosticListener, Collections.emptySet(), Collections.emptySet(),
// to make this a stand-alone example, I use embedded source code
Collections.singleton(new SimpleJavaFileObject(
URI.create("string:///Class1.java"), Kind.SOURCE) {
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return "package test;\npublic class Class1 { public class Inner {} }";
}
}));
if(task.call()) try {
URLClassLoader cl = new URLClassLoader(new URL[]{ binaryDirectory.toUri().toURL() });
Class<?> loadedClass = cl.loadClass("test.Class1");
System.out.println("loaded "+loadedClass);
System.out.println("inner classes: "+Arrays.toString(loadedClass.getClasses()));
} catch(ClassNotFoundException ex) {
ex.printStackTrace();
}
在上面的示例中,我们知道了类目录的根,因为已经定义了它。这允许简单地使用现有的URLClassLoader
而不是实现新型的类加载器。当然,使用自定义文件管理器,我们还可以将内存存储用于临时目录。
您可以使用此API来发现生成的内容,这使您可以使用生成的类而无需事先知道要编译的源文件中存在哪些包或内部类声明。
public static Class<?> compile( DiagnosticListener<JavaFileObject> diagnosticListener,
Locale locale, String sourceFile) throws IOException, ClassNotFoundException {
JavaCompiler c = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm
= c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());
// define where to store compiled class files - use a temporary directory
Path binaryDirectory = Files.createTempDirectory("compile-test");
fm.setLocation(StandardLocation.CLASS_OUTPUT,
Collections.singleton(binaryDirectory.toFile()));
JavaCompiler.CompilationTask task = c.getTask(null, fm,
diagnosticListener, Collections.emptySet(), Collections.emptySet(),
fm.getJavaFileObjects(new File(sourceFile)));
if(task.call()) {
Class<?> clazz = null;
URLClassLoader cl = new URLClassLoader(new URL[]{binaryDirectory.toUri().toURL()});
for(JavaFileObject o: fm.list(
StandardLocation.CLASS_OUTPUT, "", Collections.singleton(Kind.CLASS), true)) {
String s = binaryDirectory.toUri().relativize(o.toUri()).toString();
s = s.substring(0, s.length()-6).replace('/', '.');
clazz = cl.loadClass(s);
while(clazz.getDeclaringClass() != null) clazz = clazz.getDeclaringClass();
if(Modifier.isPublic(clazz.getModifiers())) break;
}
if(clazz != null) return clazz;
throw new ClassNotFoundException(null,
new NoSuchElementException("no top level class generated"));
}
throw new ClassNotFoundException(null,
new NoSuchElementException("compilation failed"));
}
如果使用此方法动态绑定插件或模块,则可以扩展搜索以查找实现特定接口或具有特定批注的结果类。
以上是 Java JavaCompiler.run()也可以编译匿名类 的全部内容, 来源链接: utcz.com/qa/404415.html