如何在类加载器中使用Scala类作为共享根对象?

时间:2013-01-22 20:05:50

标签: java scala classloader

我在Scala中实现了一个自定义类加载器,用于隔离主应用程序中的插件。目前我需要一个Java接口作为共享的根对象,所以我的主应用程序可以使用插件代码。

作为共享根的接口(我希望这是Scala):

public interface Handler {
    public List<HandlerInfo> getHandlers();
}

示例插件:

class MyPlugin extends Handler {
  def getHandlers: java.util.List[HandlerInfo] = // get some handlers
}

申请中的用法:

val jarFile     = new File(System.getProperty("user.dir") + "/plugins/" + jarName)
val cl          = new PluginLoader(jarFile, this.getClass.getClassLoader) // my custom classloader
val classToLoad = Class.forName(className, true, cl)
val handler     = classToLoad.newInstance.asInstanceOf[Handler]
val handlers    = handler.getHandlers

这很好用,但我的问题是我必须保留这个Java类(以及生成的构建配置)。我想改为使用Scala特征或抽象类,如下所示:

trait Handler {
  def getHandlers : List[HandlerInfo]
}

然后我的插件看起来像这样:

class MyPlugin extends Handler {
  def getHandlers: List[HandlerInfo] = // no more java.util.List
}

但我不能这样做,因为这行

val handler     = classToLoad.newInstance.asInstanceOf[Handler]

抛出一个ClassCastException,大概是因为Scala编译器没有生成一个干净的Java界面。有没有解决方法,所以我可以有一个Scala专用项目?

1 个答案:

答案 0 :(得分:1)

问题不在你想象的地方;它在你的类加载器中的某个地方。默认的类加载器工作得很好,甚至是Java。证据:

// File Handler.scala
trait Handler { def getHandlers: List[String] }

// File MyPlugin.scala
class MyPlugin extends Handler { def getHandlers = List("salmon", "cod") }

// File Interop.java
public class Interop {
  public static void main(String[] args) {
    try {
      Class classToLoad = Class.forName("MyPlugin");
      Handler handler = (Handler)classToLoad.newInstance();
      System.out.println("Class loader = "+handler.getClass().getClassLoader());
      System.out.println(handler.getHandlers());
    }
    catch (Exception e) { System.out.println("Uh-oh: "+e); }
  }
}

在这里我们运行它:

$ java -cp .:/usr/share/scala/2.10/lib/scala-library.jar Interop
Class loader = sun.misc.Launcher$AppClassLoader@1f3e8d89
List(salmon, cod)
$

使用Scala List和所有内容,以及默认Java类加载器加载的新类。