Java动态加载插件

时间:2012-12-13 23:23:45

标签: java plugins runtime classpath

我想创建一个可以动态加载插件的应用程序,但我没有在互联网上找到任何文献。

困难之处在于:我事先并不知道这个名字。

例如,我有插件界面:

public interface Plugin {
    public static Plugin newPlugin();
    public void executePlugin(String args[]);
}

这样,在jar文件中实现插件的每个类都在列表中实例化:

Method method = classToLoad.getMethod ("newPlugin");
mylist.add(method.invoke(null);
  1. 第一个问题是,我在界面中没有静态方法。
  2. 第二个问题是,我不知道如何找到实现接口的所有类
  3. 感谢您的帮助。

2 个答案:

答案 0 :(得分:23)

因此,您似乎希望在运行时动态发现实现特定接口(例如,Plugin)的类。你基本上有两种选择:

  1. 使用像osgi
  2. 这样的组件框架
  3. 使用Java的内部发现流程(ServiceLoader
  4. 由于有许多关于osgi(也是小的)的好教程,我不会在这里详述。要使用Java的内部发现过程,您需要执行以下操作:

    • 将您希望发现的所有“新”类捆绑到一个jar文件中
    • 在jar文件中创建一个新文件 META-INF/services/package.Plugin您必须在此处使用完整的包限定符
    • 此文件是一个简单的文本文件,包含该jar文件中实现Plugin的每个类的完全限定名称
    • 将该jar文件放入(可能已在运行的)应用程序的类路径中
    • 发现服务:

    服务发现是这样完成的:

    ServiceLoader<Plugin> loader = ServiceLoader.load(Plugin.class)
    for (Plugin p : loader) {
        // do something with the plugin
    }
    

    此处有更多详细信息:http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html

    对于接口中的静态方法:不可能。由于静态方法可以在没有类实例的情况下访问,并且接口只是定义方法而没有任何功能,因此它的语义也有些奇怪。因此,静态允许调用Interface.doSomething()而接口不定义任何功能,这只会导致混淆。

    编辑:

    添加了描述元文件中的内容

答案 1 :(得分:2)

关于你的第一个问题,在接口中无法使用静态方法,我的建议是简单地使用接口有一个标记并实例化它。

你的插件界面可以简单地是:

public interface Plugin {
  public void executePlugin(String args[]);
}

然后你可以这样做:

if (someClass instanceOf Plugin) {
  mylist.add(someClass.newInstance());
}

这导致了第二个问题,您将如何获得someClass参考。 没有标准的方法来查找在类路径中实现给定接口的所有类,但是,如果给定的文件,您可以执行的方法是扫描类路径中的jar以.class结尾,通过jar中的路径确定它的完全限定名称,并使用Class.forName()方法实现类。

在伪代码中是这样的:

for each jar in your classpath {
  for each file in JarFile {
    if (file ends with .class) {
       materialize class using Class.forName
     } 
  }
}

使用Class实例,您可以检查它是否正在实施您的Plugin界面。

另外请记住,如果需要向插件添加任何上下文,可以在每个接收上下文对象的插件中创建构造函数,而不是使用默认构造函数。在这种情况下,您不必使用newInstance(),而是必须通过反射获得您想要的参数的构造函数。