0

从跨应用程序/applet java 可访问性服务的角度来看,您将如何链接到一个包,但只能在运行时根据包的存在/可用性(已加载)选择性地执行操作?

我想我在这里感兴趣的是一种解决阶级认同危机的方法但不是两个应用程序共享对象之间的问题,而是在类加载器的更高级别加载的服务。似乎反射是要走的路,但我不确定如何或是否可以以这种方式实现派生类。我需要添加从特定可选类派生的特定侦听器,我可以使用小程序类加载器加载侦听器,但内部仍然失败。假设您想添加一个 JInternalFrameListener,但不保证 Swing 可用,使用反射您可以找到添加侦听器的方法,但是如果找不到任何相关类,您如何创建并让框架侦听器工作因为在基类加载器中找不到它们!我是否需要创建一个线程并将 setContextClassLoader 用于知道摆动的类加载器,以便我可以可靠地加载类?只是试图在我现有的线程上设置类加载器似乎不起作用。

问题的早期描述 对不起,我不太确定要问什么或如何说清楚,所以它有点啰嗦。

假设一个类使用另一个类的某些功能,但另一个类可能并不总是可用 - 如果这是一个 JNLP 应用程序,则假设从 JNLP 查找网站。

在某个阶段,我认为简单地针对 JNLP 进行编译将意味着除非 JNLP 可用,否则我的类将不会加载,因此为了识别这个可选部分,我只是简单地将它包裹try{} catch( NoClassDefFoundError )起来。

后来发生了一些变化(可能是更改 jdk 或 ?? 我不记得了),似乎我也应该使用try{} catch( ClassNotFoundException ).

现在我想将这个想法扩展到其他可选功能,但它似乎并不能始终如一地工作。

假设我想添加一些功能以在 JRE1.6 运行时中使用与在 JRE1.3 中运行的相同的 jar 和类来执行更高级的操作,或者说我想处理特定 gui 工具包中的一些控件,这些控件可能并不总是像 SWT 或 oracle.forms 一样使用。

有什么方法可以更可靠地做到这一点吗?导致异常并捕获它以一直忽略它似乎是错误的。

当前的问题归结为能够针对 oracle.forms 进行编译,但是安装在 ext 中的可访问性组件无法访问 oracle.forms 类,即使已经创建了 oracle.forms 包中的对象。如果我将 frmall.jar 扔到 ext 目录中进行测试,那么可访问性组件会工作到由于同一包的不同版本而导致整个批次变得脆弱的程度。

我似乎陷入了类加载器不是正确的问题(??)的问题上。我如何找到合适的?

编辑:到目前为止的答案有点有趣,但并没有让我到达我想去的地方。

对于 gui 组件,我目前以工厂的形式编译,例如...

import oracle.forms.ui.*;
import java.awt.*;
static public IComponentNode newNode( INode parent, Component component ) {
  System.out.println( component.getClass().toString() );
  try{
  if( component instanceof FormDesktopContainer )
     ... does stuff here like return new FormDesktopNode( parent, (FormDesktopContainer) component )
  } catch ( NoClassDefFoundError a ) {
    System.out.println( a.getMessage() ); 
  }

它打印出来class oracle.forms.ui.FormDesktopContainer,然后在使用 NoClassDefFound 调用 instanceof 时抛出异常,从而打印出来oracle/forms/ui/FormDesktopContainer

那么它怎么会有一个类的实例却找不到呢?

4

4 回答 4

2

这个怎么样?凌乱,但它应该工作:

public boolean exists(String className){

  try {
      Class.forName(className);
      return true;
      }
  catch (ClassNotFoundException){
      return false;
  }
}
于 2009-04-15T15:23:59.463 回答
1

您可以通过调用来检查课程的可用性

ClassLoader.getSystemClassLoader().loadClass("my.package.MyClass")

如果它抛出 ClassNotFoundException,则它不可用。如果你得到 Class 对象,它就是。然后,您可以根据该类是否可用来选择行为。

于 2009-04-15T15:23:31.897 回答
0

getSystemClass 加载器对此没有用处,因为根据给定窗口所在的小程序,可以与多个可能的类加载器进行交互。在更基类加载器上加载的可访问性组件看不到小程序特定的类。

与对象进行交互,反射完成了这项工作,尽管它确实增加了很多需要维护的东西。

// statically linking would be
return component.getText();

// dynamically is
try {
  return (String)component.getClass().getMethod("getText", new Class [] {}).invoke(component, new Object [] {});
} catch (Throwable e) {
  e.printStackTrace();
}

更棘手的一点是在编写从一个不可直接访问的接口派生的类时,使用代理服务可以实现这一点,为代理服务提供小程序特定的类加载器和接口的动态加载类。

public void addListener(Container parent) {
  if (parent == null) { return; }
  if ("oracle.forms".equals(parent.getClass().getName())) {
    // Using the class loader of the provided object in the applet
    // get the "class" of the interface you want to implement
    Class desktopListenerClass = Class.forName( "oracle.DesktopListener"
         , true, parent.getClass().getClassLoader());

    // Ask the proxy to create an instance of the class, 
    // providing your implementation through the InvocationHandler::invoke
    Object desktopListener = Proxy.newProxyInstance(
         parent.getClass().getClassLoader()
         , new Class[] { desktopListenerClass }, new InvocationHandler() {

      public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable {
        if ("functionName".equals(method.getName())) {
          // do stuff
        }
        return null;
      }
    });

    // do something with your new object
    Method addDesktopListener = parent.getClass().getMethod("");
    addDesktopListener.invoke(parent, desktopListener);
  }
}

示例缩减以显示一般方法

于 2009-09-30T02:15:07.703 回答
0

我建议根据您的最低目标编译大部分代码。将使用特定可选库的代码明确分开,但取决于您的大部分代码。一次动态加载使用可选库的代码。主类应该做一些检查在其静态初始化器中是否存在所需的库/版本。

对于 JNLP,您的 JNLP 主类静态加载 JNLP 相关代码。

(请注意,试图从正常链接的代码中捕获与类加载相关的异常是不可靠的。)

于 2009-04-15T15:31:33.017 回答