【发布时间】:2014-09-01 13:49:02
【问题描述】:
我正在尝试从 XML 文件中检索一些 Java Bean 的描述。
我想用项目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);
}
}
MemoryFileManager 在哪里:
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;
}
}
而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.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);
}
}
不幸的是,我没有得到预期的输出,而是:
import lombok.Data;
public @Data class Person {
private String name;
}
/Person.java:2: warning: Can't initialize javac processor due to (most likely) a class loader problem: java.lang.NoClassDefFoundError: com/sun/tools/javac/processing/JavacProcessingEnvironment
public @Data 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.Lombok.main(Lombok.java:42)
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
Person@39aeed2f
请注意,由于显示了典型输出,因此已编译该类并执行默认的toString() 方法。
另请注意,如果我运行前一个示例,现在我会得到以下信息:
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!
显然,通过查看异常传递的警告消息,lombok 没有正确挂钩给定的编译器。不幸的是,我找不到任何有用的信息。我只能认为这可能是lombok 没有正确处理 Java JDK 8。我说的对吗?
您知道解决此问题的任何其他方法吗?
【问题讨论】:
-
你确定你有一个 JDK 而不是 JRE 并且
tools.jar在类路径中吗? -
嗨@Holger,感谢您的回复!是的,我有 Java SDK 8,但我在 Mac 上!显然,Mac 上缺少
tools.jar(请参阅stackoverflow.com/questions/5616318/…)。感谢您为我指明正确的方向! -
这很奇怪......我刚刚检查了一下,我在
$JAVA_HOME/lib/tools.jar中有tools.jar。但是,它在 Eclipse 中不存在……这实际上是 Eclipse 的错:参考 Java 环境被配置为 JRE。包括tools.jar使一切正常。再次感谢! -
也许你必须手动将它添加到 Eclipse 项目类路径中……
-
仅供参考,最后我告诉
gradle将其作为依赖项添加到项目中: dependencies { compile files("${System.properties['java.home']}/ ../lib/tools.jar") 编译 'org.projectlombok:lombok:1.14.4' testCompile 'junit:junit:4.11' }
标签: compiler-construction java-8 lombok