Logback 无法在 JLink 构建的 JavaFX 映像中工作

问题描述 投票:0回答:1

我有一个使用 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
但没有效果。

我该如何解决这个问题?

maven javafx logback jlink
1个回答
0
投票

链接时,模块解析算法从一组根模块开始,然后递归枚举它们的

requires
指令,直到找到所有所需的模块。据我所知,根模块由
--add-modules
参数和每个
--launcher
参数(如果有)指定。一旦这些模块被解析,算法将再次递归搜索任何模块,该模块为任何已解析模块提供服务,但仅当通过时才进行搜索。如果模块未解析,无论是因为它是根模块,是根模块直接或间接需要的,还是提供服务,那么它将不会包含在运行时映像中。
provides
模块是所谓的“提供者模块”。这意味着它的主要目的是提供由另一个模块使用的服务,并且很少直接或间接需要。因此,使用 uses 创建运行时映像时不会解决该问题,除非您传递
--bind-services
或通过

ch.qos.logback.classic

显式包含它。

不幸的是,Maven 的 
javafx-maven-plugin
插件似乎没有提供指定
jlink
的方法。虽然你可以告诉它通过
--bind-services

--add-modules 虽然要在执行

--add-modules
时将
--bind-services
包含在模块路径上,但似乎您还必须设置:

<bindServices>true</bindServices>

但是,

ch.qos.logback.classic
将包括每个解析模块使用的任何服务的
all
提供者模块。这可能会导致 JDK 中包含的模块明显多于必要的模块。您可以通过传递适当的

javafx:jlink
 参数来控制这一点,但似乎没有办法传递该参数。另外,在这种情况下,使用 
<runtimePathOption>MODULEPATH</runtimePathOption>

似乎更像是一种解决方法,而不是真正的解决方案。

简而言之,你基本上有以下选择:

配置 
javafx-maven-plugin
以传递

--bind-services

并使用大于必要的运行时映像。

  1. --limit-modules 指令添加到应用程序的模块信息描述符中。

    请注意,通常应该避免需要提供程序模块。这样做会使更换提供商变得更加困难。但是,我怀疑要求 

    --limit-modules
  2. 不会在您的情况下造成重大问题,并且可能是最简单、最有效的解决方案。
  3. 使用不同的 Maven 插件来创建运行时映像,该插件可以让您更好地控制传递给 

    --bind-services

    的参数。

    
    

  4. 绑定服务示例

    这里是配置 
    javafx-maven-plugin

    以传递
  5. 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

(请注意这种方法如何导致运行时映像中的模块数量多于所需)

	

© www.soinside.com 2019 - 2024. All rights reserved.