我们正在尝试使用WebStart调试无法解决的问题,其中对Jars内部资源的访问将“随机”失败。也许每1000个应用程序运行一次将以此错误结束,这可能发生在从jar读取资源的任何地方。
在Google中搜索和Java Bug数据库没有任何相似之处(或者至少没有任何帮助)。
我们正试图通过“检测”应用程序来获取有关客户端上发生的事情的更多信息,以便我们跟踪对ClassLoader.getResource(String)
的所有调用(包括间接地通过ClassLoader.getResourceAsStream(String)
)。在不更改应用程序代码的情况下,我创建了一个“启动器”,可以使用自定义类加载器运行整个应用程序。
不幸的是,似乎我的ClassLoader被某种方式绕过了。我没有看到任何预期的System.out输出。这是我试过的:
private static final class MyClassLoader extends ClassLoader {
private MyClassLoader() {
super(TheClassThatMainIsIn.class.getClassLoader());
}
@Override
public URL getResource(String name) {
System.out.println("getResource("+name+")");
// Snip
return super.getResource(name);
}
@Override
public InputStream getResourceAsStream(String name) {
System.out.println("getResourceAsStream("+name+")");
final URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (final IOException e) {
return null;
}
}
}
public static void main(String[] args) {
System.out.println("Starting MyRealApp Launcher ...");
final MyClassLoader loader = new MyClassLoader();
try {
Class<?> realAppClasss = loader.loadClass("MyRealAppClass");
Method main = realAppClasss.getMethod("main", String[].class);
main.invoke(null, (Object) args);
} catch (final RuntimeException e) {
throw e;
} catch (final Error e) {
throw e;
} catch (final InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new UndeclaredThrowableException(cause);
} catch (final Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
我在这里做错了什么?
答案 0 :(得分:2)
是。这原则上有效。
但是,您需要考虑资源加载代码如何获取类加载器。由于类没有显示,看起来它们使用父类加载器。 您需要考虑不同的情况:
使用上下文类加载器的代码,如:
Thread.currentThread().getContextClassLoader().getResource("via-context");
通过在调用main:
之前设置它很容易实现Thread.currentThread().setContextClassLoader(loader);
Method main = realAppClasss.getMethod("main", String[].class);
main.invoke(null, (Object) args);
接下来你要考虑的是从当前类中获取类加载器并加载它的代码。当您通过父类加载器加载类时,它还将使用该类加载器来获取资源。像:
MyRealAppClass.class.getResource("via-class");
MyRealAppClass.class.getClassLoader().getResource("via-class");
objectInfApp.getClass().getClassLoader().getResource("via-class");
要避免您必须确保应用程序类实际上是使用类加载器加载的,而不是父类。对于一个简单的main,您可以从URL类加载器扩展,跳过任何父级和用户URL的原始类路径。像:
// URL class loader to lookup in jars etc
private static class MyClassLoader extends URLClassLoader
{
public MyClassLoader(URL[] urls) {
// Use the given URLs and skip any parent class loader, directly go to the system loader
super(urls,null);
}
// ...
// Then setup the class path
String[] classPath = System.getProperty("java.class.path").split(";");
URL[] classPathUrls = new URL[classPath.length];
for (int i = 0; i < classPath.length; i++) {
classPathUrls[i] = new File(classPath[i]).toURL();
}
MyClassLoader loader = new MyClassLoader(classPathUrls);
这应涵盖最基本的案例。当你真正的应用程序本身有更多的类加载器技巧时,你可能需要设置更多。