【问题标题】:How to use an object instantiated by a different ClassLoader如何使用由不同 ClassLoader 实例化的对象
【发布时间】:2014-12-11 23:06:50
【问题描述】:

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

欢迎提出任何建议。谢谢。

【问题讨论】:

    标签: java jar applet classloader classcastexception


    【解决方案1】:

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

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

    如果您必须让外部组件查找您的小程序并在其上调用方法,则使用已用于应用程序生命周期方法的相同模型修改或子类化 JCL 以提供额外的 shim 方法。

    【讨论】:

    • 也许小程序需要接受来自 Javascript 的调用?
    • 对了 biziclop,我直接从 Javascript 调用 action1() 和 action2()。
    • Javascript 调用 applet 方法通常会变得一团糟,但是是的,我想这可能是正在发生的事情。我猜其他小程序可能也在做同样的事情(也是一团糟)。
    • 已编辑为将接口暴露给外部组件的情况添加建议。
    • 我不确定您对其他 shim 方法的含义。您是否建议我将 action1() 和 action2() 添加到 JCL(的子类),然后让 JCL 在这些方法中调用 start()/stop()?这里的重点是我需要将网页中的数据输入到小程序中,并将结果(可能是大量数据)返回网页。我可以在 start() 之前设置参数(比如我在网页中使用 ),但我无法从中获取数据,可以吗?
    【解决方案2】:

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

    你需要做的是这样的:

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

    JarClassLoader 应该调用the appropriate super constructor of ClassLoader。这样,可以由“主”类加载器加载的任何类都只会加载一次,因此对接口的强制转换应该可以工作。

    更新:如果无法绕过JarClassLoaders 的非授权行为,那么您的下一个最佳选择是反思。这不是很好,但至少它有效。

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

    【讨论】:

    • 不幸的是,JarClassLoader 似乎 没有 正确地委托给其父 ClassLoader(默认为 ClassLoader.getSystemClassLoader()),因为记录的限制包括它可能从系统内置库以外的位置加载系统类。讨厌。
    • 可以想象,接口解决方案无论如何都会起作用。
    • 在这种情况下,反射。
    • 我在上面的源代码中添加了 JCL 构造函数;)。我会尝试按照您的建议传递父类加载器,看看是否可行。
    • 我按照您的建议调用了 JCL 构造函数,但仍然是那个讨厌的强制转换异常。即使我在实例化 JCL this.getClass().getClassLoader().loadClass("MyInterface"); 之前显式加载接口类,我也会收到异常。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-15
    • 2015-04-02
    • 2011-04-10
    相关资源
    最近更新 更多