Compiler construction 使用'lombok'注释和JavaJDK8在内存中编译Java类

Compiler construction 使用'lombok'注释和JavaJDK8在内存中编译Java类,compiler-construction,java-8,lombok,Compiler Construction,Java 8,Lombok,我试图从XML文件中检索一些JavaBean的描述。 我想用projectlombok中的@Data对它们进行注释,以自动包括构造函数、equals、hashCode、getter、setter和toString。 我想在内存中编译它们,生成几个实例(使用来自同一XML文件的数据),然后将它们添加到Drools中,最终对这些数据进行一些推理 不幸的是,我无法编译这些类,因此我请求您的帮助 以下代码显示了如何在内存中以编程方式编译Java类: package example; import ja

我试图从XML文件中检索一些JavaBean的描述。 我想用project
lombok
中的
@Data
对它们进行注释,以自动包括构造函数、equals、hashCode、getter、setter和toString。 我想在内存中编译它们,生成几个实例(使用来自同一XML文件的数据),然后将它们添加到Drools中,最终对这些数据进行一些推理

不幸的是,我无法编译这些类,因此我请求您的帮助

以下代码显示了如何在内存中以编程方式编译Java类:

package example;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;

public class Simple {

    public static void main(String[] args) throws Exception {
        String name = "Person";
        String content = //
            "public class " + name + " {\n" + //
            "    @Override\n" + //
            "    public String toString() {\n" + //
            "        return \"Hello, world!\";\n" + //
            "    }\n" + //
            "}\n";
        System.out.println(content);

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null));

        List<String> options = new ArrayList<String>();
        options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));

        List<JavaFileObject> files = new ArrayList<JavaFileObject>();
        files.add(new MemoryJavaFileObject(name, content));

        compiler.getTask(null, manager, null, options, null, files).call();

        Object instance = manager.getClassLoader(null).loadClass(name).newInstance();
        System.out.println(instance);
    }

}
MemoryJavaClassObject
是:

package example;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

import javax.tools.SimpleJavaFileObject;

public class MemoryJavaClassObject extends SimpleJavaFileObject {

    protected final ByteArrayOutputStream stream = new ByteArrayOutputStream();

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

    public byte[] getBytes() {
        return stream.toByteArray();
    }

    @Override
    public OutputStream openOutputStream() throws IOException {
        return stream;
    }

}
最后,
MemoryJavaFileObject
是:

package example;

import java.io.IOException;
import java.security.SecureClassLoader;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;

public class MemoryFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {

    private MemoryJavaClassObject object;

    public MemoryFileManager(StandardJavaFileManager manager) {
        super(manager);
    }

    @Override
    public ClassLoader getClassLoader(Location location) {
        return new SecureClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                byte[] b = object.getBytes();
                return super.defineClass(name, object.getBytes(), 0, b.length);
            }
        };
    }

    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String name, Kind kind, FileObject sibling) throws IOException {
        object = new MemoryJavaClassObject(name, kind);
        return object;
    }

}
package example;

import java.net.URI;

import javax.tools.SimpleJavaFileObject;

public class MemoryJavaFileObject extends SimpleJavaFileObject {

    private CharSequence content;

    protected MemoryJavaFileObject(String className, CharSequence content) {
        super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.content = content;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return content;
    }

}
如果在第一个代码块中运行该示例,将获得以下输出,如预期的那样:

public class Person {
    @Override
    public String toString() {
        return "Hello, world!";
    }
}

Hello, world!
现在,如果我将
lombok.jar
添加到我的项目中,并包括以下示例:

package example;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;

public class Lombok {

    public static void main(String[] args) throws Exception {
        String name = "Person";
        String content = //
            "import lombok.Data;\n" + //
            "public @Data class " + name + " {\n" + //
            "    private String name;\n" + //
            "}\n";
        System.out.println(content);

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null));

        List<String> options = new ArrayList<String>();
        options.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));

        List<JavaFileObject> files = new ArrayList<JavaFileObject>();
        files.add(new MemoryJavaFileObject(name, content));

        compiler.getTask(null, manager, null, options, null, files).call();

        Object instance = manager.getClassLoader(null).loadClass(name).newInstance();
        System.out.println(instance);
    }

}
显然,通过查看异常传递的警告消息,
lombok
没有正确地钩住给定的编译器。不幸的是,我找不到任何有用的信息。我只能认为这可能是
lombok
没有正确处理javajdk8。我说得对吗


你知道解决这个问题的其他方法吗?

多亏了霍尔格,我成功地解决了这个问题

问题是由于类路径中缺少
tools.jar
。 这是因为Eclipse默认将Java环境识别为JRE而不是JDK

最重要的是,JavaJDK可能有
tools.jar
文件,也可能没有,这取决于您使用的版本

如果您有Java7或Java8,您应该在
$Java_HOME/lib/tools.jar
中有这样的库

如果您有Java 6,则该文件不存在,但相同的功能由
$Java_HOME/Classes/Classes.jar提供

编译器是Java 6中添加的一项功能,因此如果您想要使用它,并且您有一个较旧版本的Java,您应该首先更新您的环境

现在,有几种方法可以将
tools.jar
(或
classes.jar
)包含到项目的类路径中;由于我使用gradle,我决定将其作为依赖项引入,正如您在以下代码片段中所看到的:

dependencies {
    compile files("${System.properties['java.home']}/../lib/tools.jar")
    compile 'org.projectlombok:lombok:1.14.4'
    testCompile 'junit:junit:4.11'
}
希望这个小小的解释能帮助其他面临类似问题的人


干杯

你确定你有JDK而不是JRE,并且
tools.jar
在类路径中吗?嗨@Holger,谢谢你的回复!是的,我有JavaSDK8,但我在Mac上!显然,Mac上缺少
tools.jar
(请参阅)。谢谢你给我指明了正确的方向!这很奇怪。。。我刚刚检查了一下,在
$JAVA\u HOME/lib/tools.jar
中有
tools.jar
。但是,它在Eclipse中不存在。。。这实际上是Eclipse的错误:参考Java环境被配置为JRE。包括
tools.jar
可以让一切正常工作。再次感谢!也许您必须手动将其添加到Eclipse项目类路径中…仅供参考,最后我告诉
gradle
将其作为依赖项添加到项目中:依赖项{compile files(${System.properties['java.home']}/./lib/tools.jar”)编译“org.projectlombok:lombok:1.14.4”testCompile“junit:junit:4.11”},以防在这3年内发生变化:是否开发了更干净的方法来解决此问题?我现在在IntelliJ上有一个类似的问题,我已经发布了一个引用您的问题。不知道,我已经在一段时间内转移到Python。。。顺便说一句,语言不错!在我的梦中,我又开始工作了。我从来没有遇到过这样的问题。顺便说一句,像添加依赖项这样的常见补丁对我不起作用。
public class Person {
    @Override
    public String toString() {
        return "Hello, world!";
    }
}

/Person.java:1: warning: Can't initialize javac processor due to (most likely) a class loader problem: java.lang.NoClassDefFoundError: com/sun/tools/javac/processing/JavacProcessingEnvironment
public class Person {
       ^
      at lombok.javac.apt.Processor.init(Processor.java:84)
      at lombok.core.AnnotationProcessor$JavacDescriptor.want(AnnotationProcessor.java:87)
      at lombok.core.AnnotationProcessor.init(AnnotationProcessor.java:141)
      at com.sun.tools.javac.processing.JavacProcessingEnvironment$ProcessorState.<init>(JavacProcessingEnvironment.java:500)
      at com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors$ProcessorStateIterator.next(JavacProcessingEnvironment.java:597)
      at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:690)
      at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
      at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
      at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
      at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1173)
      at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:859)
      at com.sun.tools.javac.main.Main.compile(Main.java:523)
      at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
      at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
      at example.Simple.main(Simple.java:44)
  Caused by: java.lang.ClassNotFoundException: com.sun.tools.javac.processing.JavacProcessingEnvironment
      at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
      at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
      at java.security.AccessController.doPrivileged(Native Method)
      at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
      at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
      at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
      ... 15 more
1 warning
Hello, world!
dependencies {
    compile files("${System.properties['java.home']}/../lib/tools.jar")
    compile 'org.projectlombok:lombok:1.14.4'
    testCompile 'junit:junit:4.11'
}