我正在实现一个核心部分与GUI分离并在运行时作为服务加载的程序。为了在运行时发现实现,我花了几个星期的时间。为了尝试隔离问题,我从这里选择了一个最小的ServiceLoader示例https://reflectoring.io/service-provider-interface,并将其扩展到我的项目结构中。我的项目的GUI需要这个插件,但运行ServiceLoader的代码不需要。我使用的是org.openjfx.javafxplugin
的0.0.10版本,最后一个版本是0.0.13,但这会导致另一个问题,即主类再也找不到了。如果build.gradle中没有请求该插件,ServiceLoader代码将工作,加载实现,程序将给出预期的输出。当build.gradle中请求javaxplugin时,程序不再工作。
有人有什么建议吗?我真的被卡住了,因为这是一个JavaFX应用程序,我需要那个插件。
该项目是一个Gradle项目,包含3个子项目(模块):API、实现(核心)和应用程序(GUI)。相关文件如下。
美国石油module-info.java:
module tlapi {
exports com.chesolver.spi;
exports com.chesolver;
}
奇怪的是,如果我启用javafx插件,编译器会引发错误com.chesolver.spi.Library: module tlapi does not declare 'uses'
,这对我来说很奇怪,因为模块tlapi是api,而com.chesolver.spi.Library
是此模块中包含的接口的一部分。
API版本.gradle:
plugins {
id 'tubesheetlayout.java-library-conventions'
}
核心模块信息:
module tlcore {
requires tlapi;
}
核心建筑。Gradle:
plugins {
id 'tubesheetlayout.java-library-conventions'
id 'org.javamodularity.moduleplugin' version '1.8.9'
}
dependencies {
implementation project(':api')
}
应用模块信息:
module tlclient {
requires tlapi;
}
应用程序构建器。gradle:
plugins {
id 'tubesheetlayout.java-application-conventions'
// *NOTICE* if uncommented the ServiceLoader code does not work
//id 'org.openjfx.javafxplugin' version '0.0.13'
//id 'org.javamodularity.moduleplugin' version '1.8.9'
}
dependencies {
implementation project(':api')
implementation project(':core')
}
application {
mainClass = "com.chesolver.library.LibraryClient"
}
1条答案
按热度按时间2ul0zpep1#
您的主要问题(错误地指定服务模块)以及如何解决该问题的资源
对于Java平台模块化应用程序,需要在模块中使用Java平台模块化服务定义。这意味着在
module-info.java
中使用provides
和uses
语句。您的代码和service tutorial you linked并没有将服务集成到Java平台模块系统中。教程中讨论的Maven模块系统(可能还有Gradle模块)是完全不同的。
要了解有关模块化服务的信息,请阅读以下网址中的“服务”部分:
module-info.java
的provides
和uses
语句。ServiceLoader
javadoc,应彻底审查和理解。Baeldung为模块化服务教程提供了example code,这比我在这个答案中提供的要简单。但是Baeldung教程没有演示绑定多个服务实现、jlink或使用从JavaFX应用程序加载的服务模块,这就是为什么我在这里添加了这些内容的示例。
重要建议
关于这一点,我的建议是:* 不要使用服务机制,除非你知道你需要它 *。
示例解决方案
我知道这个问题是专门针对Gradle的,但我实际上并不使用该工具。我将使用Maven提供一个替代解决方案。它的某些方面将直接移植到Gradle,而其他方面则需要您进行调整。
解决方案由一个多模块得Maven项目组成,每个子模块对应一个Java平台模块,有一个父pom.xml指定所有得子模块,所有得子模块继承自父pom.xml。
所涉及的子模块如下:
ShapeFactory
接口,可以创建形状。ShapeFactory
实现。ShapeFactory
实现。ShapeFactory
服务提供程序并使用它们生成形状。从组合框中选择形状工厂服务提供程序,然后单击“Create Shape”,所选提供程序将用于生成形状,然后显示该形状。
我会张贴在这里的代码,不幸的是,有很多它:-(
您可以将maven项目从根目录导入到Idea IDE中。该项目将作为单个Idea项目加载,其中包含多个Idea项目模块。当您从IDE运行主
ShapeApplication
类时,IDE将自动生成所有模块并为应用程序提供服务。要构建所有内容,请在项目的根目录下运行
mvn clean install
。要创建jlinked应用,请转到shape-app
目录并运行mvn javafx:jlink
。.iml
只是idea模块项目文件,您可以忽略它们。父项 pom.xml
第一个
第一个
第一个
第一个
注意事项
像这样把东西分成模块和服务会使事情变得更加复杂。
很多小事情都可能出错,尤其是如果忘记更改名称,复制和粘贴时出现的错别字。
由于服务模块是动态发现的,因此编译器不会发现一些拼写错误。
您不能在模块间混用套件名称。
使用jlink进行链接比较麻烦,因为您需要确保服务位于jlinked模块路径上,并且您需要指示jlink绑定它们。
这些工具在错误消息中有点迟钝。
如果配置不正确,将找不到模块,系统将认为没有可用的匹配服务。
实际上,在每次链接之前,将 * 所有 * 模块安装到Maven库中,否则它可能会拾取旧版本或找不到您的软件(这可能实际上没有必要,但在我的经验中似乎是这样)。
如果你只使用默认的
bind-services
选项,绑定服务会使你的jlink图像变得很大。显然你可以只绑定列出的服务,而不是所有的服务,但是我找不到如何使用javafx-maven-plugin来实现这一点。你可以使用jlink命令行来获得比maven插件更细粒度的控制,尽管这会更痛苦。如果您没有在javafx-maven-plugin中显式地放置
MODULEPATH
设置,它将不会找到您的服务模块。请始终检查jlink详细输出,以确保绑定了所有预期得服务.
当处理像这样的多模块项目时,我强烈建议将所有模块保持在同一版本号(在父pom.xml中集中配置),否则很容易链接到过时的版本。不过,这不是这个示例项目的设置方式。要固定版本,请在父pom.xml中定义一个
shape.version
属性,只要存在1.0-SNAPSHOT
,将其替换为${shape-version}
,则所有项目将始终使用相同的版本。代码假设所有的东西都在模块化环境中运行,没有任何东西从类路径运行。如果你想让服务在类路径上运行,那么你需要做更多的工作(例如在
MANIFEST.MF
文件中定义服务元数据)。我建议只支持100%模块化环境,除非你绝对也必须支持类路径的执行。我相信还有更微妙和潜在的问题,我没有在这里提到。