如何解决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:
客户端 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 举报,一经查实,本站将立刻删除。