如何使用由不同ClassLoader实例化的对象

时间:2014-10-16 14:24:46

标签: java jar applet classloader classcastexception

我的applet需要属于外部jar(签名jar)和本机库的类。我在applet.jar中部署这些jar。要从这些jar中加载类,我发现com.jdotsoft.jarloader.JarClassLoader类很有用。我首先构建一个控制台应用程序来测试我的applet的功能,JarClassLoader对我来说很好。 接下来我用另外两个公共方法写了MyApplet类,让我们说一下action1()和action2()。而且,正如JarClassLoader所描述的,我编写了一个MyAppletLauncher类。 (见下面的代码)。为了获得对MyApplet对象的引用,由JarClassLoader实例化,我将getApplet()方法添加到JarClassLoader类。

MyApplet.java:

public class MyApplet extends Applet {
    public void init()    { ... }
    public void start()   { ... }
    public void stop()    { ... }
    public void destroy() { ... }

    public String action1() {
        ...
    }
    public int action2() {
        ...
    }
}

MyAppletLauncher.java:

public class MyAppletLauncher extends Applet {
    private JarClassLoader jcl;
    public void init() {
        jcl = new JarClassLoader();
        jcl.initApple("MyApplet", this);
    }
    public void start() {
        jcl.startApplet();
    }
    public void stop() {
        jcl.stopApplet();
    }
    public void destroy() {
        jcl.destroyApplet();
    }
    public String action1() {
        return ((MyApplet) jcl.getApplet()).action1(); // <-- ClassCastException
    }
    public int action2() {
        return ((MyApplet) jcl.getApplet()).action2(); // <-- ClassCastException
    }
}

我对JarClassLoader.java的更改:

public class JarClassLoader extends ClassLoader {

    private Applet applet; // was JApplet

    public JarClassLoader() {
        this(ClassLoader.getSystemClassLoader());
    }

    public JarClassLoader(ClassLoader parent) {
        super(parent);

        ...
    }

    public void initApplet(String sClass, final Applet appletParent) { // 2nd arg was JApplet
        Class<?> clazz = loadClass(sClass);
        applet = (Applet) clazz.newInstance();
        ...
    }

    public Applet getApplet() {
        return applet;
    }
    ...

在我看来,很明显MyAppletLauncher的实例无法将提供的Applet实例向下转换为MyApplet,因为MyApplet实例是在不同的命名空间中创建的(=不是由同一个类加载器创建的)。但我的问题是如何获取对MyApplet对象的action1()和action2()调用?或者更常见的是,我可以从MyAppletLauncher对象调用MyApplet的公共接口吗?

我已经尝试了几件事,比如

  • MyApplet实现MyInterface,然后强制转换为MyInterface
  • 将成员JarClassLoader.applet的类型更改为MyApplet

但是在某些时候都会导致ClassCastException。

欢迎任何建议。谢谢。

2 个答案:

答案 0 :(得分:1)

我认为你错过了启动器小程序的重点。它一方面充当applet容器及其系统ClassLoader之间的垫片,另一方面充当JCL加载的真实applet和JCL。除了applet生命周期方法之外,启动器小程序中应该没有任何内容。

我不确定如何在启动器applet上调用除生命周期方法之外的任何方法。这不是偶然的,所以就这样停止尝试。在真正的applet类中实现所有applet的功能以及所需的任何帮助程序类,就好像启动程序applet不存在一样。

如果您必须让外部组件查找您的applet并在其上调用方法,那么使用与已经用于应用程序生命周期方法的相同模型修改或子类化JCL以提供其他填充程序方法。

答案 1 :(得分:0)

我们无法看到您的代码的一部分,但如果您的JarClassLoader链正确地链接到父类加载器(它可能没有),那么接口解决方案应该可行。前提是接口由主类加载器加载,而不是从自定义外部jar加载。

你需要做的是这样的事情:

public void init() {
    jcl = new JarClassLoader(this.getClass().getClassLoader());
    jcl.initApplet("MyApplet", this);
}

JarClassLoader应致电the appropriate super constructor of ClassLoader。通过这种方式可以加载&#34; main&#34;类加载器只会加载一次,因此转换为接口应该可以工作。

更新:如果无法解决JarClassLoader非委派行为,那么您的下一个最佳选择就是反思。这不好,但至少它有效。

public void action1() {
    Applet applet = jcl.getApplet();
    Method action = applet.getClass().getDeclaredMethod( "action1" );
    action.invoke( applet );
}