Pav*_* S. 7 java compiler-construction classloader
我正在尝试使用Java Compiler API来编译一些java类.该类从jar文件导入一些包,这些包可以由上下文ClassLoader加载,让我们称他为X,这不是系统类加载器.当我运行编译时,编译器抱怨没有识别导入.我试图指定fileManager来传递类加载器,但它没有帮助.
当调用compile方法时,它首先打印"CLASS LOADED",因此上下文ClassLoader可以找到依赖类.但是,编译本身失败(我收到"Compilation FAILED"消息)并且在编译期间我得到如下错误:
/path/to/my/Source.java:3:包my.dependency不存在import my.dependency.MyClass; ^
我究竟做错了什么?将自定义类加载器传递给compilationTask的正确方法是什么?我不能从ClassLoader中提取URL,因为它不是URLClassLoader.
我的方法在这里:
public void compile(List<File> filesToCompile) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager stdFileManager =
compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> fileObjects = stdFileManager
.getJavaFileObjectsFromFiles(filesToCompile);
FileManagerImpl fileManager = new FileManagerImpl(stdFileManager);
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, fileObjects);
Boolean result = task.call();
if (result == true) {
System.out.println("Compilation has succeeded");
} else {
System.out.println("Compilation FAILED");
}
}
private final class FileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {
public FileManagerImpl(JavaFileManager fileManager) {
super(fileManager);
}
@Override
public ClassLoader getClassLoader(JavaFileManager.Location location) {
ClassLoader def = getContextClassLoader();
try {
def.loadClass("my.dependency.MyClass");
System.out.println("CLASS LOADED");
} catch (ClassNotFoundException ex) {
System.out.println("NOT LOADED");
}
return def;
}
}
Run Code Online (Sandbox Code Playgroud)
重点是,当类加载器加载类时,javac将调用JavaFileManager#list()以获取包中所有文件的列表.
因此,要使用自定义类加载器,您需要修改(或扩展)它以覆盖JavaFileManager#list().希望您可以重用一些用于类加载的逻辑.
您可能希望使用自己的实现JavaFileObject来表示类对象.然后您需要覆盖JavaFileManager#inferBinaryName()(否则javac版本将崩溃).您的实现JavaFileObject也需要覆盖(至少)JavaFileObject#openInputStream.
以下是一些提示:http://atamur.blogspot.be/2009/10/using-built-in-javacompiler-with-custom.html
此外,不要让你的生活比它应该更难,并延长ForwardingJavaFileManager和SimpleJavaFileObject.
作为参考,这是一个示例实现:
@Override public Iterable<JavaFileObject> list(Location location,
String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)
throws IOException
{
Iterable<JavaFileObject> stdResults =
fileManager.list(location, packageName, kinds, recurse);
if (location != StandardLocation.CLASS_PATH
|| !kinds.contains(JavaFileObject.Kind.CLASS))
{
return stdResults;
}
Set<JavaFileObject> additional = pkgObjects.get(packageName);
if (additional == null || additional.isEmpty()) {
return stdResults;
}
List<JavaFileObject> out = new ArrayList<>();
for (JavaFileObject obj : additional) {
out.add(obj);
}
for (JavaFileObject obj : stdResults) {
out.add(obj);
}
return out;
}
Run Code Online (Sandbox Code Playgroud)
pkgObjects从包名到的地图在哪里JavaFileObject.填充此映射的方式取决于类加载器的工作方式.