我正在写一个插件加载器 - 它加载不在类路径上的jar。我写了一个简单的自定义ClassLoader,它在构造函数中接受一个JarFile,并在JarFile中查找指定的类。这个加载器简单地覆盖了ClassLoader的findClass()
方法,并且工作正常。自定义ClassLoader与资源加载
然后我确定我还需要能够从插件jar中获取资源。所以我重覆findResource()
。这有意想不到的结果是导致基类插件类无法在jar中找到其他类:我得到了NoClassDefFoundErrors!
换句话说,如果我有plugin.jar包含为myplugin和MyPluginComponent:
- ,如果我不重写
findResource()
,那么我可以加载的jar,创建为myplugin的实例,而这又可以创建一个MyPluginComponent。但是我找不到在jar中捆绑的资源。 - 如果我做覆盖
findResource()
,那么我可以加载的jar,创建为myplugin,的实例,但如果为myplugin试图创建一个MyPluginComponent,我得到一个NoClassDefFoundError错误。
这表明findResource()的实现无法找到类文件 - 但它甚至没有被调用(根据我的日志记录),所以我不明白这是怎么回事。这种互动如何解决,我该如何解决?
我试着编写一个小型自包含示例,并且手动生成一个不会产生“不兼容幻数”错误的jar文件时遇到了困难。希望无论我做错了什么,都会从单独的类加载器中看出来。不便之处,感谢您的时间。
import com.google.common.io.CharStreams;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.lang.ClassLoader;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Custom class loader for loading plugin classes. Adapted from
* http://kalanir.blogspot.com/2010/01/how-to-write-custom-class-loader-to.html
*/
public static class PluginLoader extends ClassLoader {
private JarFile jarFile_;
public PluginLoader(JarFile jarFile) {
super(Thread.currentThread().getContextClassLoader());
jarFile_ = jarFile;
}
@Override
public Class findClass(String className) {
try {
// Replace "." with "/" for seeking through the jar.
String classPath = className.replace(".", "/") + ".class";
System.out.println("Searching for " + className + " under " + classPath);
JarEntry entry = jarFile_.getJarEntry(classPath);
if (entry == null) {
return null;
}
InputStream stream = jarFile_.getInputStream(entry);
String contents = CharStreams.toString(
new InputStreamReader(stream));
stream.close();
byte[] bytes = contents.getBytes();
Class result = defineClass(className, bytes, 0, bytes.length);
return result;
}
catch (IOException e) {
System.out.println(e + "Unable to load jar file " + jarFile_.getName());
}
catch (ClassFormatError e) {
System.out.println(e + "Unable to read class data for class " + className + " from jar " + jarFile_.getName());
}
return null;
}
@Override
protected URL findResource(String name) {
System.out.println("Asked to find resource at " + name);
try {
String base = new File(jarFile_.getName()).toURI().toURL().toString();
URL result = new URL(String.format("jar:%s!/%s", base, name));
System.out.println("Result is " + result);
return result;
}
catch (IOException e) {
System.out.println(e + "Unable to construct URL to find " + name);
return null;
}
}
@Override
public InputStream getResourceAsStream(String name) {
System.out.println("Getting resource at " +name);
JarEntry entry = jarFile_.getJarEntry(name);
if (entry == null) {
System.out.println("Couldn't find resource " + name);
return null;
}
try {
return jarFile_.getInputStream(entry);
}
catch (IOException e) {
System.out.println(e + "Unable to load resource " + name + " from jar file " + jarFile_.getName());
return null;
}
}
}
为什么不使用'URLClassLoader'有什么具体原因吗?它给你所有这些功能。 – KDM
@KDM,因为我不知道该怎么办。这很奇怪,因为我在这方面做了很多搜索。由于最近从度假返回,我会把它归咎于愚蠢。 – chris