Java 使用反射和类加载器创建类实例时的ClassCastException
首先,这是Java1.4(项目限制)。 我正在尝试创建一个应用程序管理器。 它使用自己的自定义类加载器实例加载每个应用程序的主类。 然后,它使用反射创建主类的实例。 每个应用程序都实现一个公共接口,因此在创建实例后,它会运行应用程序的预定义方法 然而,我在崩溃点1遇到了一些问题(参见代码)。该类未被识别为其接口的一个实现。 如果我找到这个代码块,我会在崩溃点2得到ClassCastException 我想这两个错误都与同一个问题有关(当然) 有人能帮我吗? 代码的相关部分如下(已删除导入) 非常感谢 马库斯 //AppManager.javaJava 使用反射和类加载器创建类实例时的ClassCastException,java,reflection,classloader,classcastexception,Java,Reflection,Classloader,Classcastexception,首先,这是Java1.4(项目限制)。 我正在尝试创建一个应用程序管理器。 它使用自己的自定义类加载器实例加载每个应用程序的主类。 然后,它使用反射创建主类的实例。 每个应用程序都实现一个公共接口,因此在创建实例后,它会运行应用程序的预定义方法 然而,我在崩溃点1遇到了一些问题(参见代码)。该类未被识别为其接口的一个实现。 如果我找到这个代码块,我会在崩溃点2得到ClassCastException 我想这两个错误都与同一个问题有关(当然) 有人能帮我吗? 代码的相关部分如下(已删除导入) 非常
public class AppManager {
public ThreadGroup threadGroup;
private Class appClass;
private AppInstance appInst;
public AppContextImpl context;
private AppManager(CustomClassLoader cl, String mainClass) throws ClassNotFoundException {
final String className = mainClass;
final CustomClassLoader finalLoader = cl;
appClass = cl.loadClass(mainClass);
// DEBUG CODE:
Class[] k1 = AppInstance.class.getInterfaces();
System.out.println(k1.length + " interfaces for AppInstance.class:");
for (int ii = 0; ii < k1.length; ii++) {
System.out.println(" " + ii + " - " + k1[ii].getName() + " (" + k1[ii].getClassLoader() + ")");
}
Class[] k2 = appClass.getInterfaces();
System.out.println(k2.length + " interfaces for appClass instance:");
for (int ii = 0; ii < k2.length; ii++) {
System.out.println(" " + ii + " - " + k2[ii].getName() + " (" + k2[ii].getClassLoader() + ")");
}
// CRASH POINT 1
if (!(AppInstance.class.isAssignableFrom(appClass))) {
throw new IllegalArgumentException("Attempt to run a non-AppInstance class: " + appClass);
}
context = new AppContextImpl(mainClass, this);
cl.setAppManager(this);
Constructor m;
try {
m = appClass.getConstructor(new Class[0]);
// CRASH POINT 2
appInst = (AppInstance) m.newInstance(new Object[0]);
appInst.init(context);
} catch (Exception e) {
System.out.println("Got ClassCastException here!\n");
e.printStackTrace();
}
}
public static void main(String[] args) {
App app1;
String path1 = "/home/user/workspace/MultiTaskTest/bin/";
String app1Name = "App1";
Vector v1 = new Vector();
try {
v1.add(new URL(path1));
} catch (MalformedURLException e1) {
final File file1 = new File(path1);
try {
URL path1aux = (URL) AccessController.doPrivileged(
new PrivilegedExceptionAction() {
public Object run() throws IOException {
if (!file1.exists()) {
System.out.println("Warning: \"" + file1.getPath() + "\" not found");
return null;
}
return file1.toURI().toURL();
}
});
if (path1aux != null) {
v1.add(path1aux);
}
} catch (PrivilegedActionException e) {
e.getException().printStackTrace();
}
}
final URL[] array1 = (URL[]) v1.toArray(new URL[v1.size()]);
CustomClassLoader cl1 = (CustomClassLoader) AccessController.doPrivileged(
new PrivilegedAction() { public Object run() {
return new CustomClassLoader(array1);
}});
System.out.println("ClassLoader 1 created: " + cl1);
try {
app1 = new App(cl1, app1Name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("Cannot find class App1.");
}
}
}
//App1.java
public class App1 implements AppInstance {
private AppContextImpl contextObj;
public void init(AppContextImpl context) {
this.contextObj = context;
System.out.println("Running App1...");
}
}
//AppContextImpl.java
public class AppContextImpl {
public String mainClass;
public AppManager app;
public AppContextImpl(String mainClass, AppManager app) {
this.mainClass = mainClass;
this.app = app;
}
}
//CustomClassLoader.java
public class CustomClassLoader extends URLClassLoader {
AppManager appInst;
public CustomClassLoader(URL[] paths) { super(paths, null); }
public void setAppManager(AppManager app) { this.appInst = app; }
}
AppManager.java文件中调试代码的输出为:
0 interfaces for AppInstance.class:
1 interfaces for appClass instance:
0 - AppInstance (CustomClassLoader@480457)
您的AppInstance类可能由每个自定义类加载器单独加载。因为类对象依赖于实际的类和类加载器,所以它们实际上是不同的类。 因此,类加载器1中的AppInstance与类加载器2中的AppInstance不同 您需要做的是使用标准的类加载器层次结构:为应用程序使用根类加载器,并确保类加载器可以加载AppInstance。然后从根目录创建自定义类加载器子目录。当他们需要访问AppInstance类时,他们将使用从根目录加载的内容 因此,与此相反:
public CustomClassLoader(URL[] paths) { super(paths, null); }
您需要为CustomClassLoader提供一个父类看起来这是一个类加载器问题。您必须确保加载的类以及AppInstance接口本身都是使用相同的类加载器加载的,否则Java会认为它们完全不相关。class.getGenericInterfaces()和class.getInterfaces()的输出是什么?Lord.Quackstar:再次检查问题。我已经插入了一些注释来提供您要求的信息。Eyal:我会对类加载器做更多的研究。我一有消息就回来。ThanksEyal:这确实是一个类装入器问题。请阅读我对纪尧姆回答的评论。你知道有没有可能解决这些问题?再次感谢。分离类装入器提供的隔离正是我们的目标。如果我为它们设置父类加载器,隔离就会被打破,不是吗?我将进一步研究类加载器,以确保我没有误解某些东西。我很快就回来。同时,感谢您的回答。您需要决定应用程序的哪些部分是共同点,哪些部分是特定于模块的。似乎您正在尝试重新设计OSGi,您可能需要对此进行研究;)我已经查过OSGi是什么了。我需要做的就是这样。但是,我不能使用OSGi,因为我的应用程序基于一个公共定义的规范,该规范施加了一些限制。我需要使用自己的自定义类加载器加载每个应用程序。在那之后,我将在公共场所保持一个监视线程。应用程序将使用一个特殊的API在它们之间进行交互。您知道是否可以在不为每个自定义类加载器设置父类加载器的情况下解决崩溃点(1和2)中的问题?再次感谢。根据您所说的,公共API应该由单个类加载器加载,并且该类加载器应该是模块类加载器的父类。因为父类加载器看不到模块类(只有API),所以无法加载它们;它们将在模块级加载。我不是Xlet的专家,它似乎是JME基类的一部分,可能是由引导类加载程序加载的。来自基本JRE的类总是由引导加载,因此它们不可能被复制。
public CustomClassLoader(URL[] paths) { super(paths, null); }