Java JavaCompiler.run()也可以编译匿名类 [英] Java JavaCompiler.run() compiling anonymous classes as well

查看:111
本文介绍了Java JavaCompiler.run()也可以编译匿名类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图动态加载文本文件并进行编译.

I am trying to load in text files on the fly and compile them.

File file = new File("Files/"+fileName+".java");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, errStream, file.getAbsolutePath());

然后我将在以后加载已编译的.class文件:

I then will load the compiled .class files later:

 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的成员

I am currently running into two issues: The first is if the .java files I load in contain anonymous classes. It doesn't appear that the JavaCompiler class will compile these. Exception in thread "main" java.lang.IllegalAccessException: Class Loader.ClassLoader can not access a member of class Files.myname.myclass$1 with modifiers ""

第二个: 有时候我会因为NoClassDefFoundError错误而出错: 线程主"中的异常java.lang.NoClassDefFoundError:Files/myname/myclass 尽管事实是其他类将正确加载,并且.class文件在该路径中.

The second: Is that sometimes I will get errors for NoClassDefFoundError: Exception in thread "main" java.lang.NoClassDefFoundError: Files/myname/myclass Despite the fact that other classes will load correctly and the .class file is in that path.

推荐答案

显然,您的loadStrategyClass是在自定义ClassLoader中定义的.问题在于,仅对感兴趣的类调用一次defineClass是不够的,您的类加载器通常必须通过实现findClass才能按需解析类,以便JVM可以解析依赖项,例如内部类.

Apparently, your loadStrategyClass is defined within a custom ClassLoader. The problem is that it is not enough to call defineClass once for the class you’re interested in, your class loader must be able to resolve classes on demand, usually by implementing findClass, so the JVM can resolve dependencies, like the inner classes.

您没有指定如何获取loadStrategyClass方法的strategyClassFile参数.由于您运行的编译器没有任何选项,因此我想您只是相对于源文件查找了文件.要解决其他依赖关系,需要知道类目录的实际根目录.当您定义存储类文件的位置时,例如

You didn’t specify, how you get the strategyClassFile argument for the loadStrategyClass method. Since you ran the compiler without any options, I suppose you simply looked up the file relative to the source file. To resolve other dependencies, the actual root of the class directory needs to be known. It becomes much easier when you define where to store the class files, e.g.

// customize these, if you want, null triggers default behavior
DiagnosticListener<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而不是实现新型的类加载器.当然,使用自定义文件管理器,我们还可以将内存存储用于临时目录.

In the example above, we know the root of the class directory, because we have defined it. This allows to simply use the existing URLClassLoader rather than implementing a new type of class loader. Of course, using a custom file manager, we also could use an in-memory storage for rather than a temporary directory.

您可以使用此API来发现生成的内容,这使您可以使用生成的类而无需事先知道要编译的源文件中存在哪些包或内部类声明.

You may use this API to discover what has been generated, which enables you to use the resulting class without knowing beforehand, which package or inner class declarations exist in the source file you’re going to compile.

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"));
}

如果使用它来动态绑定插件或模块,则可以扩展搜索以查找实现特定接口或具有特定批注的结果类.

If you use this to dynamically bind plugins or modules, you may extend the search to look for a result class which implements a particular interface or has a certain annotation.

这篇关于Java JavaCompiler.run()也可以编译匿名类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆