我有一个使用 Maven 构建的 JavaFX 应用程序,它使用
logback-classic
作为 SLF4J 提供程序。我正在使用 javafx-maven-plugin
运行应用程序并构建 jlink 图像。
当我使用
mvn javafx:run
运行应用程序时,日志记录正常。
当我使用
mvn javafx:jlink
构建图像并运行该图像时,出现错误:
SLF4J(W):未找到 SLF4J 提供商。
SLF4J(W):默认为无操作(NOP)记录器实现
SLF4J(W):请参阅 https://www.slf4j.org/codes.html#noProviders 了解更多详细信息。
看起来
logback-classic
没有正确包含在图像中。
我尝试过手动将
logback-classic-1.5.7.jar
和 logback-core-1.5.7.jar
复制到 target/image/lib
但没有效果。
我该如何解决这个问题?
链接时,模块解析算法从一组根模块开始,然后递归枚举它们的
requires
指令,直到找到所有所需的模块。据我所知,根模块由 --add-modules
参数和每个 --launcher
参数(如果有)指定。一旦这些模块被解析,算法将再次递归搜索任何模块,该模块为任何已解析模块提供服务,但仅当通过时才进行搜索。如果模块未解析,无论是因为它是根模块,是根模块直接或间接需要的,还是提供服务,那么它将不会包含在运行时映像中。provides
模块是所谓的“提供者模块”。这意味着它的主要目的是提供由另一个模块使用的服务,并且很少直接或间接需要。因此,使用 uses
创建运行时映像时不会解决该问题,除非您传递 --bind-services
或通过 ch.qos.logback.classic
显式包含它。
不幸的是,Maven 的javafx-maven-plugin插件似乎没有提供指定
jlink
的方法。虽然你可以告诉它通过 --bind-services
:
--add-modules
时将 --bind-services
包含在模块路径上,但似乎您还必须设置:
<bindServices>true</bindServices>
但是,
ch.qos.logback.classic
将包括每个解析模块使用的任何服务的all提供者模块。这可能会导致 JDK 中包含的模块明显多于必要的模块。您可以通过传递适当的
javafx:jlink
参数来控制这一点,但似乎没有办法传递该参数。另外,在这种情况下,使用
<runtimePathOption>MODULEPATH</runtimePathOption>
似乎更像是一种解决方法,而不是真正的解决方案。
简而言之,你基本上有以下选择: 配置javafx-maven-plugin以传递
--bind-services
并使用大于必要的运行时映像。
将 --limit-modules
指令添加到应用程序的模块信息描述符中。
--limit-modules
使用不同的 Maven 插件来创建运行时映像,该插件可以让您更好地控制传递给
--bind-services
的参数。
这里是配置javafx-maven-plugin 以传递
requires ch.qos.logback.classic;
模块信息.java
ch.qos.logback.classic
Main.java
jlink
聚甲醛
--bind-services
项目结构
module app {
requires javafx.controls;
requires org.slf4j;
exports com.example.app to
javafx.graphics;
}
从执行
package com.example.app;
import java.util.stream.Collectors;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main extends Application {
private static final Logger LOG = LoggerFactory.getLogger(Main.class);
@Override
public void init() {
var modules = ModuleLayer.boot()
.modules()
.stream()
.map(Module::getName)
.sorted()
.collect(Collectors.joining("\n ", "\n ", ""));
LOG.info("Resolved modules: {}", modules);
}
@Override
public void start(Stage primaryStage) {
LOG.info("Application start.");
var root = new StackPane(new Label("Hello, World!"));
primaryStage.setScene(new Scene(root, 500, 300));
primaryStage.setTitle("Example");
primaryStage.show();
}
@Override
public void stop() {
LOG.info("Application stop.");
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jfx-slf4j-test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>23</maven.compiler.release>
<slf4jVersion>2.0.16</slf4jVersion>
<logbackVersion>1.5.12</logbackVersion>
<javafxVersion>23.0.1</javafxVersion>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<runtimePathOption>MODULEPATH</runtimePathOption>
<mainClass>app/com.example.app.Main</mainClass>
<launcher>example</launcher>
<bindServices>true</bindServices>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4jVersion}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logbackVersion}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafxVersion}</version>
</dependency>
</dependencies>
</project>
<PROJECT-ROOT>
|
+---.vscode
| settings.json
|
\---src
\---main
\---java
| module-info.java
|
\---com
\---example
\---app
Main.java