具有资源加载的自定义 ClassLoader
Custom ClassLoader with resource loading
我正在编写一个插件加载程序 -- 它加载不在 class 路径上的 jar。我编写了一个简单的自定义 ClassLoader,它在其构造函数中采用 JarFile,并在 JarFile 中查找名为 class 的文件。这个加载器简单地覆盖了 ClassLoader 的 findClass()
方法,并且工作正常。
然后我确定我还需要能够从插件jar 中获取资源。所以我覆盖了findResource()
。这导致基本插件 class 无法在 jar 中找到其他 classes 的意外结果:我得到 NoClassDefFoundErrors!
换句话说,如果我有包含 MyPlugin 和 MyPluginComponent 的 plugin.jar:
- 如果我不覆盖
findResource()
,那么我可以加载 jar,创建 MyPlugin 的实例,然后创建 MyPluginComponent。但是我找不到捆绑在 jar 中的资源。
- 如果我确实覆盖
findResource()
,那么我可以加载 jar,创建 MyPlugin 的实例,但是 如果 MyPlugin 尝试创建 MyPluginComponent,我会得到一个NoClassDefFoundError.
这表明 findResource() 的实现以某种方式无法找到 class 文件——但它甚至没有被调用(根据我的日志记录),所以我不明白这是怎么回事案件。这种互动是如何产生的,我该如何解决?
我试图编写一个独立的小示例,但 运行 难以手动生成不会产生 "incompatible magic number" 错误的 jar 文件。希望无论我做错了什么,仅从 class 加载程序就可以看出。对于给您带来的不便,我们深表歉意,感谢您的宝贵时间。
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;
}
}
}
如果您的要求只是阅读额外的 JAR 文件,扩展 URLClassLoader
可能是更好的选择。
public class PluginLoader extends URLClassLoader {
public PluginLoader(String jar) throws MalformedURLException {
super(new URL[] { new File(jar).toURI().toURL() });
}
}
我正在编写一个插件加载程序 -- 它加载不在 class 路径上的 jar。我编写了一个简单的自定义 ClassLoader,它在其构造函数中采用 JarFile,并在 JarFile 中查找名为 class 的文件。这个加载器简单地覆盖了 ClassLoader 的 findClass()
方法,并且工作正常。
然后我确定我还需要能够从插件jar 中获取资源。所以我覆盖了findResource()
。这导致基本插件 class 无法在 jar 中找到其他 classes 的意外结果:我得到 NoClassDefFoundErrors!
换句话说,如果我有包含 MyPlugin 和 MyPluginComponent 的 plugin.jar:
- 如果我不覆盖
findResource()
,那么我可以加载 jar,创建 MyPlugin 的实例,然后创建 MyPluginComponent。但是我找不到捆绑在 jar 中的资源。 - 如果我确实覆盖
findResource()
,那么我可以加载 jar,创建 MyPlugin 的实例,但是 如果 MyPlugin 尝试创建 MyPluginComponent,我会得到一个NoClassDefFoundError.
这表明 findResource() 的实现以某种方式无法找到 class 文件——但它甚至没有被调用(根据我的日志记录),所以我不明白这是怎么回事案件。这种互动是如何产生的,我该如何解决?
我试图编写一个独立的小示例,但 运行 难以手动生成不会产生 "incompatible magic number" 错误的 jar 文件。希望无论我做错了什么,仅从 class 加载程序就可以看出。对于给您带来的不便,我们深表歉意,感谢您的宝贵时间。
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;
}
}
}
如果您的要求只是阅读额外的 JAR 文件,扩展 URLClassLoader
可能是更好的选择。
public class PluginLoader extends URLClassLoader {
public PluginLoader(String jar) throws MalformedURLException {
super(new URL[] { new File(jar).toURI().toURL() });
}
}