微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Java 9+ 模块的动态加载

如何解决Java 9+ 模块的动态加载

我一直在尝试使用 Java 11.0.2 编写一个程序,该程序从指定目录加载插件(作为 jar)。为此,我遵循了 ZhekaKozov 在他对 How do I dynamically load modules from a directory in Java 9+

的回答中建议的代码

就我而言,我使用的是 Java 9+ 模块,其中一个模块用于应用程序,另一个用于加载的插件。第三个模块提供了一些其他两个可以访问的通用代码。我怀疑是模块化导致了我遇到的问题。在我将提供的简单示例中,我运行该应用程序,该应用程序作为一个窗口打开,其中包含一个包含按钮的面板。当我单击按钮时,插件应该作为服务加载并运行,向面板添加第二个按钮。单击该按钮时,会打开一个窗口以显示它正在工作。

问题是,当我从 jar 运行应用程序并单击第一个按钮时,出现以下异常:

com.example.dynamic.feature    
Exception in thread "JavaFX Application Thread" java.lang.module.FindException: Module javafx.graphics not found,required by com.example.dynamic.feature
        at java.base/java.lang.module.Resolver.findFail(Resolver.java:877)
        at java.base/java.lang.module.Resolver.resolve(Resolver.java:191)
        at java.base/java.lang.module.Resolver.resolve(Resolver.java:140)
        at java.base/java.lang.module.Configuration.resolve(Configuration.java:411)
        at java.base/java.lang.module.Configuration.resolve(Configuration.java:245)
        at com.recite.dynamic.client.Client.loadplugins(Client.java:94)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
        at com.sun.glass.ui.invokelaterdispatcher$Future.run(invokelaterdispatcher.java:96)
        at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
        at java.base/java.lang.Thread.run(Thread.java:834)

我运行它:

java -jar com.example.dynamic.client.jar

应用程序(客户端)的代码是:

package com.example.dynamic.client;

<numerous imports> ...

public class Client extends Application
{
    private Stage stage;
    private HBox basePane;

    @FXML
    private Button btnLoadplugins;

    public static void main(String[] args)
    {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception
    {
        this.stage = stage;

        try
        {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/com.example.dynamic.toolbar/fxml/client.fxml"));
            loader.setController(this);

            basePane = loader.load();

            Scene scene = new Scene(basePane);

            stage.setScene(scene);
            stage.setAlwaysOnTop(true);

            stage.setonCloseRequest(event -> close());
            btnLoadplugins.setonAction(actionEvent -> Platform.runLater(this::loadplugins));

            stage.show();
        }
        catch (IOException e)
        {
            e.printstacktrace();
        }
    }

    private void close()
    {
        stage.hide();

        Platform.exit();
        System.exit(0);
    }

    private void loadplugins()
    {
        Path pluginsDir = Paths.get("E:\\DynamicModules\\MinimalExample\\DynamicMinimalExample\\plugins");

        ModuleFinder pluginsFinder = ModuleFinder.of(pluginsDir);

        List<String> plugins = pluginsFinder
                .findAll()
                .stream()
                .map(ModuleReference::descriptor)
                .map(ModuleDescriptor::name)
                .collect(Collectors.toList());

        plugins.forEach(s -> System.out.println(s));

        Configuration pluginsConfiguration = ModuleLayer
                .boot()
                .configuration()
                .resolve(pluginsFinder,ModuleFinder.of(),plugins);

        System.out.println("create module layer");

        ModuleLayer layer = ModuleLayer
                .boot()
                .defineModulesWithOneLoader(pluginsConfiguration,ClassLoader.getSystemClassLoader());

        System.out.println("loading services");

        List<IFeatureAccessor> services = ServiceLoader
                .load(layer,IFeatureAccessor.class)
                .stream()
                .map(ServiceLoader.Provider::get)
                .collect(Collectors.toList());

        //Stop the same service from installing twice. I'm sure there's a far better solution
        services.forEach(iFeatureAccessor -> {
            Set<String> ids = new HashSet<>();

            basePane.getChildren().forEach(button -> {
                if (button.getId() != null)
                {
                    ids.add(button.getId());
                }
            });

            if (!ids.contains(iFeatureAccessor.getButton().getId()))
            {
                basePane.getChildren().add(iFeatureAccessor.getButton());
            }
        });
    }
}

module com.example.dynamic.feature {
    requires javafx.fxml;
    requires javafx.graphics;
    requires javafx.controls;
    requires com.example.dynamic.generic;

    exports com.example.dynamic.feature;
    provides com.example.dynamic.generic.IFeatureAccessor with com.example.dynamic.feature.FeatureController;
}

客户端从一个简单的启动器类运行并且运行良好,直到我点击加载插件

通用代码为:

package com.example.dynamic.generic;

import javafx.scene.control.Button;

public interface IFeatureAccessor
{
    Button getButton();
    void toggle();
}

module com.example.dynamic.generic {
    exports com.example.dynamic.generic;
    requires javafx.controls;
}

插件功能)是:

package com.example.dynamic.feature;

import com.example.dynamic.generic.IFeatureAccessor;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;

public class FeatureController implements IFeatureAccessor
{
    private final Button button;

    private VBox basePane;
    private Scene scene;
    private Stage stage;


    public FeatureController()
    {
        button = new Button("Feature");
        button.setId("com.example.dynamic.feature");

        button.setonAction(event -> toggle());

        createScene();
    }

    private void createScene()
    {
        try
        {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/com.example.dynamic.feature/fxml/feature.fxml"));
            loader.setController(this);

            basePane = loader.load();

            scene = new Scene(basePane);
        }
        catch (IOException e)
        {
            e.printstacktrace();
        }
    }

    @Override
    public Button getButton()
    {
        return button;
    }

    @Override
    public void toggle()
    {
        if (stage == null)
        {
            stage = new Stage();

            stage.setTitle(((Stage) getButton().getScene().getwindow()).getTitle());
            stage.setScene(scene);
            stage.setAlwaysOnTop(true);
            stage.initOwner(getButton().getScene().getwindow());

            stage.setonCloseRequest(event -> disable());
        }

        if(stage.isShowing())
        {
            disable();
        }
        else
        {
            enable();
        }
    }

    private void enable()
    {
        stage.show();
    }

    private void disable()
    {
        stage.hide();
    }
}

module com.example.dynamic.feature {
    requires javafx.fxml;
    requires javafx.graphics;
    requires javafx.controls;
    requires com.example.dynamic.generic;

    exports com.example.dynamic.feature;
    provides com.example.dynamic.generic.IFeatureAccessor with com.example.dynamic.feature.FeatureController;
}

jar 是在 IntelliJ Idea 中从“具有依赖项的模块”创建的。下图显示功能 jar:

Construction of the feature jar

客户端 jar 以类似的方式构建,添加了清单和定义的主类。

将 javafx.base 添加一个或两个 jar 没有区别。

如果我创建一个没有 javafx 或通用模块的功能 jar,并通过 IntelliJ Idea 运行应用程序,它会加载 jar 并按预期添加其按钮。当我尝试使用从 jar 运行的客户端的缩减 jar 时,它抱怨找不到 javafx 或通用代码,所以我添加了它们,如图所示。这解决了问题,但给出了这个问题顶部提到的例外。

我们可以看到问题出在哪里,正如预期的那样打印出特征名称,但从未到达打印“创建模块层”的行。

(注意:通过 IDE 运行应用程序并加载按照图像构建的功能 jar 会导致不同的异常:

Exception in thread "JavaFX Application Thread" java.lang.module.ResolutionException: Module com.example.dynamic.feature contains package javafx.collections,module javafx.base exports package javafx.collections to com.example.dynamic.feature

).

如果有人能让我知道我哪里出错了,我将不胜感激。在我看来,功能 jar 应该从客户端获取其 javafx 和通用代码

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。