有没有办法在不更新主应用程序的情况下更新模块化库?

Is there a way to update modular library without updating main application?

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

在旧的实现方式中,我只是发送新库进行更新,主应用程序从文件夹中读取所有库,它工作得很好。但是在模块化系统中,如果我的应用程序想要使用我发送的新模块化库,它需要更新其模块信息文件,我需要为模块化库和主应用程序发送更新。 试想一下,chrome 需要为每个创建的新插件发送浏览器更新。如我所见,使用 Java 模块化系统您无法创建模块化应用程序。

有没有办法在不更新主应用程序或其他方式的情况下导入新模块?

Java 有一个 class:ServiceLoader.

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

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

然后您的应用程序会声明它在自己的应用程序中使用该服务 module-info:

uses com.john.myapp.PluginProvider;

并且您的应用程序代码将为 ServiceLoader 创建一个 ModuleFinder that looks in the directory (or directories) where you expect those plugin modules to reside, then pass that ModuleFinder to a Configuration which can be used to create a ModuleLayer

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();
    }
}