【问题标题】:java classloader and runtime compilationjava类加载器和运行时编译
【发布时间】:2010-12-11 04:28:18
【问题描述】:

尽管有人警告我放弃目前的行动方案,但我目前认为没有更好的方法来解决我的问题。我必须 在运行时生成 Java代码,然后编译、加载并引用它

问题是生成的代码导入了系统类加载器已经加载的代码(我想)——也就是说,代码存在于我的类路径中的一个 jar 中。 (我在 Java 6 上的 Tomcat 6 Web 容器中运行。)您可能会问自己为什么这是一个问题 - 当然不知道 - 但事实是我得到了编译错误:

/W:/.../parser/v0.5/AssignELParser.java:6: 包 com.xxx.yyy.zzz.configuration 不存在

根据互联网上的一些示例,我定义了以下类:

class MemoryClassLoader extends ChainedAction {

    private static final Logger LOG = Logger.getLogger(MemoryClassLoader.class);

    private LoaderImpl impl;

    private class LoaderImpl extends ClassLoader {

        // The compiler tool
        private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        // Compiler options
        private final Iterable<String> options = Arrays.asList("-verbose");

        // DiagnosticCollector, for collecting compilation problems
        private final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

        // Our FileManager
        private final MemoryFileManager manager = new MemoryFileManager(this.compiler);

        public LoaderImpl(File sourceDirectory) {

            List<Source> list = new ArrayList<Source>();

            File[] files = sourceDirectory.listFiles(new FilenameFilter() {

                @Override
                public boolean accept(File dir, String name) {

                    return name.endsWith(Kind.SOURCE.extension);
                }
            });

            for (File file : files) {
                list.add(new Source(file));
            }

            CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, list);
            Boolean compilationSuccessful = task.call();

            LOG.info("Compilation has " + ((compilationSuccessful) ? "concluded successfully" : "failed"));

            // report on all errors to screen
            for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
                LOG.warn(diagnostic.getMessage(null));
            }
        }

        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            synchronized (this.manager) {
                Output output = manager.map.remove(name);
                if (output != null) {
                    byte[] array = output.toByteArray();
                    return defineClass(name, array, 0, array.length);
                }
            }
            return super.findClass(name);
        }
    }

    @Override
    protected void run() {  

        impl = new LoaderImpl(new File(/* Some directory path */));

    }
}



class MemoryFileManager extends ForwardingJavaFileManager<JavaFileManager> {

    final Map<String, Output> map = new HashMap<String, Output>();

    MemoryFileManager(JavaCompiler compiler) {
        super(compiler.getStandardFileManager(null, null, null));
    }

    @Override
    public Output getJavaFileForOutput(Location location, String name, Kind kind, FileObject source) {

        Output output = new Output(name, kind);
        map.put(name, output);

        return output;
    }

}


class Output extends SimpleJavaFileObject {

    private final ByteArrayOutputStream baos = new ByteArrayOutputStream();

    Output(String name, Kind kind) {
        super(URI.create("memo:///" + name.replace('.', '/') + kind.extension), kind);
    }

    byte[] toByteArray() {
        return this.baos.toByteArray();
    }

    @Override
    public ByteArrayOutputStream openOutputStream() {
        return this.baos;
    }
}



class Source extends SimpleJavaFileObject {


    public Source(File file) {
        super(file.toURI(), Kind.SOURCE);
    }


    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {

        StringBuilder sb = new StringBuilder("");
        try {
            File file = new File(uri);
            FileReader fr = new FileReader(file);
            BufferedReader br = new BufferedReader(fr);

            sb = new StringBuilder((int) file.length());
            String line = "";
            while ((line = br.readLine()) != null) {
                sb.append(line);
                sb.append("\n");
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return sb.toString();
    }
}

似乎内部类 LoaderImpl 通过扩展 ClassLoader 类并且不调用显式超级构造函数应该引用系统类加载器作为其父类加载器。

如果是这样,那么为什么我会收到“运行时”编译错误 - 上面?为什么找不到导入类的代码?

【问题讨论】:

    标签: java compiler-construction code-generation runtime classloader


    【解决方案1】:

    不确定它是否有帮助,但您是否尝试过明确指定类路径?

    getClassPath()
    {
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      URL[] urls = ((URLClassLoader) classLoader).getURLs();
      StringBuilder buf = new StringBuilder(1000);
      buf.append(".");
      String separator = System.getProperty("path.separator");
      for (URL url : urls) {
          buf.append(separator).append(url.getFile());
      }
    }
    
    classPath = buf.toString();
    

    然后

    options.add("-classpath");
    options.add(getClassPath());
    

    我也看不到你在哪里将LoaderImpl 实例传递给compiler。不应该明确地做吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-05
      • 1970-01-01
      • 2011-08-14
      • 2019-08-18
      • 1970-01-01
      • 2019-07-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多