在Java中实现动态插件
我想在 Java 应用程序中实现动态插件功能。理想情况下:
- 应用程序将使用
getCapability()
等方法定义一个接口Plugin
。 - 插件是一个 JAR
pluginX.jar
,其中包含一个实现Plugin
的类PluginXImpl
(也许还有其他一些)。 - 用户可以将
pluginX.jar
放在一个特殊的目录中或设置一个指向它的配置参数。用户不一定必须在其类路径中包含pluginX.jar
。 - 应用程序将找到
PluginXImpl
(可能通过 JAR 清单,也可能通过反射)并将其添加到注册表中。 - 客户端可以获取
PluginXImpl
的实例,例如,通过调用getPluginWithCapability("X")
之类的方法。用户不一定必须知道插件的名称。
我感觉我应该能够使用 peaberry 做到这一点,但我不能对文档没有任何意义。我已经投入了一些时间来学习 Guice,所以我的首选答案不是“使用 Spring 动态模块”。 ”
谁能给我一个简单的想法,告诉我如何使用 Guice/peaberry、OSGi 或普通的 Java 来完成此操作?
I'd like to implement a dynamic plugin feature in a Java application. Ideally:
- The application would define an interface
Plugin
with a method likegetCapabilities()
. - A plugin would be a JAR
pluginX.jar
containing a classPluginXImpl
implementingPlugin
(and maybe some others). - The user would put
pluginX.jar
in a special directory or set a configuration parameter pointing to it. The user should not necessarily have to includepluginX.jar
in their classpath. - The application would find
PluginXImpl
(maybe via the JAR manifest, maybe by reflection) and add it to a registry. - The client could get an instance of
PluginXImpl
, e.g., by invoking a method likegetPluginWithCapabilities("X")
. The user should not necessarily have to know the name of the plugin.
I've got a sense I should be able to do this with peaberry, but I can't make any sense of the documentation. I've invested some time in learning Guice, so my preferred answer would not be "use Spring Dynamic Modules."
Can anybody give me a simple idea of how to go about doing this using Guice/peaberry, OSGi, or just plain Java?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
使用普通的 Java 方法实际上非常容易:
由于您不希望用户在启动应用程序之前配置类路径,因此我首先创建一个 URLClassLoader,其中包含指向插件目录中文件的 URL 数组。使用 File.listFiles 查找所有插件 jar,然后使用 File.toURI().toURL() 获取每个文件的 URL。您应该将系统类加载器 (ClassLoader.getSystemClassLoader()) 作为父级传递给 URLClassLoader。
如果插件 jar 包含 META-INF/services 中的配置文件(如 java.util.ServiceLoader 的 API 文档中所述),您现在可以使用 ServiceLoader.load(Plugin.class, myUrlClassLoader) 为您的插件接口获取服务加载器并对其调用 iterator() 以获取所有配置的插件实现的实例。
您仍然必须提供自己的包装器来过滤插件功能,但我想这应该不会太麻烦。
This is actually quite easy using plain Java means:
Since you don't want the user to configure the classpath before starting the application, I would first create a URLClassLoader with an array of URLs to the files in your plugin directory. Use File.listFiles to find all plugin jars and then File.toURI().toURL() to get a URL to each file. You should pass the system classloader (ClassLoader.getSystemClassLoader()) as a parent to your URLClassLoader.
If the plugin jars contain a configuration file in META-INF/services as described in the API documentation for java.util.ServiceLoader, you can now use ServiceLoader.load(Plugin.class, myUrlClassLoader) to obatin a service loader for your Plugin interface and call iterator() on it to get instances of all configured Plugin implementations.
You still have to provide your own wrapper around this to filter plugin capabilites, but that shouldn't be too much trouble, I suppose.
如果您想在运行时替换插件以在 24/7 环境中修复错误,OSGI 会很好。我用 OSGI 玩了一段时间,但花了太多时间,因为这不是必需的,而且如果你删除一个捆绑包,你需要一个 b 计划。
我的简单解决方案是,提供一个带有插件描述符类的类名的属性文件,并让服务器调用它们进行注册(包括查询它们的功能)。
这显然不是最理想的,但我迫不及待地想阅读已接受的答案。
OSGI would be fine if you want to replace the plugins during runtime i.g. for bugfixes in a 24/7 environment. I played a while with OSGI but it took too much time, because it wasn't a requirement, and you need a plan b if you remove a bundle.
My humble solution then was, providing a properties files with the class names of plugin descriptor classes and let the server call them to register (including quering their capabilities).
This is obvious suboptimal but I can't wait to read the accepted answer.
您有机会利用服务提供商接口吗?
Any chance you can leverage the Service Provider Interface?
使用 Guice 实现插件的最佳方式是使用多重绑定。链接页面详细介绍了如何使用多重绑定来托管插件。
The best way to implement plug-ins with Guice is with Multibindings. The linked page goes into detail on how to use multibindings to host plugins.
如果您知道这一点,请道歉,但请查看 类。它至少在 JDBC 中用于通过类名动态加载特定于 DBMS 的驱动程序类运行时。
然后我想枚举目录中的所有类/jar 文件,加载每个文件,并为静态方法
getCapability()
(或您选择的任何名称)定义一个接口并不困难以对您的系统有意义的任何术语和格式返回其功能/描述。Apologize if you know this, but check out the
forName
method of Class. It is used at least in JDBC to dynamically load the DBMS-specific driver classes runtime by class name.Then I guess it would not be difficult to enumerate all class/jar files in a directory, load each of them, and define an interface for a static method
getCapabilities()
(or any name you choose) that returns their capabilities/description in whatever terms and format that makes sense for your system.