使用反射和类加载器创建类的实例时出现 ClassCastException
首先,这是Java 1.4(项目限制)。 我正在尝试创建一个应用程序管理器。 它使用自己的自定义类加载器实例加载每个应用程序的主类。 之后,它使用反射创建主类的实例。 每个应用程序都实现一个公共接口,因此在创建实例后,它会运行应用程序的预定义方法。
但是,我在 CRASH POINT 1 处遇到了一些麻烦(请参阅代码)。该类不被识别为其接口的一种实现。 如果我注释此代码块,我会在崩溃点 2 处收到 ClassCastException。
我认为这两个错误都与同一问题相关(当然)。
有人可以帮助我吗? 代码的相关部分如下(导入已删除)...
非常感谢。
Marcus
// AppManager.java
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.");
}
}
}
// AppInstance.java
public interface AppInstance {
public void init(ContextImpl context);
}
// 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)
First of all, this is Java 1.4 (project restrictions).
I'm trying to create a application manager.
It loads each application's main class using it's own instance of a custom classloader.
After that, it creates an instance of the main class using reflection.
Each application implements a common interface so after the instance is created, it runs a predefined method of the application.
However, I'm having some trouble at CRASH POINT 1 (see code). The class is not recognized as one implementation of it's interface.
If I coment this code chunk, I get ClassCastException at CRASH POINT 2.
I suppose both errors are related to the same issue (of course).
Can anyone help me?
The relevant part of the code follows (imports are removed)...
Thanks very much.
Marcus
// AppManager.java
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.");
}
}
}
// AppInstance.java
public interface AppInstance {
public void init(ContextImpl context);
}
// 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; }
}
The output for the Debug code in the AppManager.java file is:
0 interfaces for AppInstance.class:
1 interfaces for appClass instance:
0 - AppInstance (CustomClassLoader@480457)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的 AppInstance 类可能由每个自定义类加载器单独加载。由于类对象取决于实际的类和类加载器,因此它们实际上是不同的类。
因此,类加载器 1 中的 AppInstance 与类加载器 2 中的 AppInstance 不同。
您需要做的是使用标准类加载器层次结构:为您的应用程序使用根类加载器,并确保 AppInstance 可由该类加载器加载。然后从根目录创建自定义类加载器子级。每当他们需要访问 AppInstance 类时,他们都会使用从根加载的内容。
因此,而不是这样:
您需要为 CustomClassLoader 提供一个父级
Your AppInstance class is probably loaded separately by each custom classloader. Since class objects depend on the actual class AND on the classloader, they are really different classes.
So AppInstance from classloader 1 is not the same as AppInstance from classloader 2.
What you need to do is using the standard classloader hierarchy: use a root classloader for your application, and male sure that AppInstance is loadable by the classloader. Then make your custom classloader children from the root. Whenever they need to access the AppInstance class, they will use what is loaded from the root.
So, instead of this:
You need to give a parent to your CustomClassLoader