【问题标题】:Java dynamically load pluginJava动态加载插件
【发布时间】:2012-12-13 23:23:44
【问题描述】:

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

困难的是:我事先不知道名字。

例如我有一个插件界面:

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

让jar文件中实现Plugin的每一个类都在一个列表中实例化:

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

感谢您的帮助。

【问题讨论】:

  • 是否有一个配置文件指定加载哪些插件来解决您的问题?
  • 嗨 Mac,在我的情况下,我想避免使用配置文件,只要文件夹中存在 jar 尝试加载它。谢谢。

标签: java plugins runtime classpath


【解决方案1】:

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

  1. 使用像 osgi 这样的组件框架
  2. 使用 Java 的内部发现过程 (ServiceLoader)

由于 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(),而接口没有定义任何功能,这只会导致混乱。

编辑:

添加描述元文件中应该包含的内容

【讨论】:

  • 我认为没有人说欢迎您 SirRichie,但您非常欢迎来到这个社区。对你的所有四个答案都投了赞成票:)
  • 通常你只是在第一行缩进 4 个空格,在你跳过一行之后。然后在另一个空行之后,您从位置 0 开始其余的普通文本。您可以用 `` 字符将代码括起来作为内联代码。
  • 是的...我做到了。现在我发现如果代码紧跟在列表项之后,这不起作用。所以我在那里放了一些填充文字,现在它就像一个魅力。
  • 嘿,这正是我正在寻找的东西,它就像一个魅力! :) 非常感谢!
  • 虽然接口不能有静态方法,但抽象类可以,抽象类可以像接口一样使用 SPI jar。 (令人愉快的简短)SPI jar 规范是here
【解决方案2】:

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

你的插件界面可以很简单:

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

然后你可以这样做:

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

这就引出了第二个问题,你将如何获得someClass 引用。 没有标准方法可以在类路径中找到所有实现给定接口的类,但是,如果给定文件,您可以做的一种方法是扫描类路径中的 jars以.class 结尾,通过它在ja​​r 中的路径确定它的完整限定名,并使用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()

【讨论】:

    猜你喜欢
    • 2011-07-22
    • 2013-08-15
    • 2011-08-22
    • 2017-08-03
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-12
    相关资源
    最近更新 更多