有没有一种方法可以在不更新主应用程序的情况下更新模块化库?

时间:2019-05-16 13:12:02

标签: java java-module

在组织模块化应用程序之前,我有一个主要的JavaFX应用程序,该应用程序加载了自定义创建的多个库,用于针对主应用程序中的不同选项和可能性。

在旧的实现方式中,我只是发送新的库以进行更新,主应用程序从文件夹中读取所有库,并且它的工作原理很吸引人。但是在模块化系统中,如果我的应用程序要使用我发送的新模块化库,它需要更新其module-info文件,但我需要为模块化库和主应用程序发送更新。 试想像,chrome需要为创建的每个新插件发送浏览器更新。如我所见,使用Java模块化系统无法创建模块化应用程序。

有没有一种方法可以导入新模块而不更新主应用程序或其他方法?

1 个答案:

答案 0 :(得分:0)

Java为此提供了一个类:ServiceLoader

如果我们假设您有一个名为PluginProvider的“服务提供者”接口,则其他模块可以通过将其放在各自的module-info.java描述符中来声明自己提供该服务:

provides com.john.myapp.PluginProvider with com.library.MyProvider;

您的应用程序随后将声明它在自己的模块信息中使用了该服务:

uses com.john.myapp.PluginProvider;

您的应用程序的代码将创建一个ModuleFinder,该目录会在您希望这些插件模块驻留的目录中查找,然后将ModuleFinder传递给Configuration,该目录可用于创建ModuleLayer用于ServiceLoader:

public class PluginLoader {

    private final ServiceLoader<PluginProvider> loader;

    public PluginLoader() {

        Path pluginDir = Paths.get(System.getProperty("user.home"),
            ".local", "share", "MyApplication", "plugins");

        ModuleLayer layer = PluginProvider.class.getModule().getLayer();
        layer = layer.defineModulesWithOneLoader(
            layer.configuration().resolveAndBind(
                ModuleFinder.of(),
                ModuleFinder.of(pluginDir),
                Collections.emptySet()),
            PluginProvider.class.getClassLoader());

        loader = ServiceLoader.load(layer, PluginProvider.class);
    }

    public Stream<PluginProvider> getAll() {
        return loader.stream();
    }

    public void reload() {
        loader.reload();
    }
}

您甚至可能想watch插入新文件或已删除文件的插件目录:

try (WatchService watch = pluginDir.getFileSystem().newWatchService()) {

    pluginDir.register(watch,
        StandardWatchEventKinds.ENTRY_CREATE,
        StandardWatchEventKinds.ENTRY_DELETE,
        StandardWatchEventKinds.ENTRY_MODIFY,
        StandardWatchEventKinds.OVERFLOW);

    WatchKey key;
    while ((key = watch.take()).isValid()) {
        loader.reload();
        key.reset();
    }
}