英文:
Which dependency is my duplicate Slf4j binding coming from in this maven dependency tree?
问题
我已经连续一个星期了,试图解释这个警告,让我不停地敲着头在我的桌子上。
当我尝试运行我的多模块Spring/Kotlin/Maven应用程序时,我在运行输出中收到以下通知:
SLF4J: 类路径包含多个SLF4J绑定。
SLF4J: 在 [jar:file:/home/vboxuser/Scratch/demo-server/controller/target/controller-0.1.0.jar!/BOOT-INF/classes!/org/slf4j/impl/StaticLoggerBinder.class] 找到绑定。
SLF4J: 在 [jar:file:/home/vboxuser/Scratch/demo-server/controller/target/controller-0.1.0.jar!/BOOT-INF/lib/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class] 找到绑定。
SLF4J: 请参阅 http://www.slf4j.org/codes.html#multiple_bindings 以了解说明。
SLF4J: 实际绑定类型为 [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
在这里,我们可以看到有两个绑定来自我的控制器模块,但我很难确定它们都来自哪里。
我的项目结构如下,总共有3个POM文件:
parent
|--controller
|--utilities
这是我的控制器模块的Maven依赖树:
[INFO] --- maven-dependency-plugin:3.3.0:tree (default-cli) @ controller ---
[INFO] app.demo.server.controller:controller:jar:0.1.0
[INFO] +- org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar:1.7.21:compile
[INFO] | +- (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - 从1.7.21管理的版本; 用于去除重复)
[INFO] | \- org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar:1.7.21:compile (从1.7.21管理的版本)
[INFO] | \- (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - 从1.7.21管理的版本; 用于去除重复)
[INFO] +- org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile
[INFO] | +- org.jetbrains.kotlin:kotlin-stdlib-common:jar:1.7.21:compile (从1.7.21管理的版本)
[INFO] | \- org.jetbrains:annotations:jar:13.0:compile
[INFO] +- org.jetbrains.kotlin:kotlin-reflect:jar:1.7.21:compile
[INFO] | \- (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - 从1.7.21管理的版本; 用于去除重复)
[INFO] \- org.springframework.boot:spring-boot-starter:jar:2.7.7:compile
[INFO] +- org.springframework.boot:spring-boot:jar:2.7.7:compile (从2.7.7管理的版本)
[INFO] | +- (org.springframework:spring-core:jar:5.3.24:compile - 从5.3.24管理的版本; 用于去除重复)
[INFO] | \- org.springframework:spring-context:jar:5.3.24:compile (从5.3.24管理的版本)
[INFO] | +- org.springframework:spring-aop:jar:5.3.24:compile (从5.3.24管理的版本)
[INFO] | | +- (org.springframework:spring-beans:jar:5.3.24:compile - 从5.3.24管理的版本; 用于去除重复)
[INFO] | | \- (org.springframework:spring-core:jar:5.3.24:compile - 从5.3.24管理的版本; 用于去除重复)
[INFO] | +- org.springframework:spring-beans:jar:5.3.24:compile (从5.3.24管理的版本)
[INFO] | | \- (org.springframework:spring-core:jar:5.3.24:compile - 从5.3.24管理的版本; 用于去除重复)
[INFO] | +- (org.springframework:spring-core:jar:5.3.24:compile - 从5.3.24管理的版本; 用于去除重复)
[INFO] | \- org.springframework:spring-expression:jar:5.3.24:compile (从5.3.24管理的版本)
[INFO] | \- (org.springframework:spring-core:jar:5.3.24:compile - 从5.3.24管理的版本; 用于去除重复)
[INFO] +- org.springframework.boot:spring-boot-autoconfigure:jar:2.7.7:compile (从2.7.7管理的版本)
[INFO] | \- (org.springframework.boot:spring-boot:jar:2.7.7:compile - 从2.7.7管理的版本; 用于去除重复)
[INFO] +- org.springframework.boot:spring-boot-starter-logging:jar:2.7.7:compile (从2.7.7管理的版本)
[INFO] | +- ch.qos.logback:logback-classic:jar:1.2.11:compile (从1.2.11管理的版本)
[INFO] | | +- ch.qos.logback:logback-core:jar:1.2.11:compile (从1.2.11管理的版本)
[INFO] | | \- org.slf4j:slf4j-api:jar:1.7.36:compile (从1.7.32管理的版本)
[INFO] | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.2:compile (从2.17.2管理的版本)
[INFO] | | +- (org.slf4j:slf4j-api:jar:1.7.36:compile - 从1.7.35管理的版本; 用于去除重复)
[INFO] | | \- org.apache.logging.log4j:log4j-api:jar:2.17.2:compile (从2.17.2管理的版本)
[INFO] |
<details>
<summary>英文:</summary>
I have been slamming my head against my desk for the past week trying to interpret this warning.
I am getting the following notification in my run output when trying to run my multi-module Spring/Kotlin/Maven application:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/vboxuser/Scratch/demo-server/controller/target/controller-0.1.0.jar!/BOOT-INF/classes!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/vboxuser/Scratch/demo-server/controller/target/controller-0.1.0.jar!/BOOT-INF/lib/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
Here we see there are two bindings coming from my controller module, but i'm having troubles determining where they are both coming from.
My project is structured like the following with 3 total pom files:
parent
|--controller
|--utilities
Here is my maven dependency tree for the controller module:
[INFO] --- maven-dependency-plugin:3.3.0:tree (default-cli) @ controller ---
[INFO] app.demo.server.controller:controller:jar:0.1.0
[INFO] +- org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar:1.7.21:compile
[INFO] | +- (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - version managed from 1.7.21; omitted for duplicate)
[INFO] | - org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar:1.7.21:compile (version managed from 1.7.21)
[INFO] | - (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - version managed from 1.7.21; omitted for duplicate)
[INFO] +- org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile
[INFO] | +- org.jetbrains.kotlin:kotlin-stdlib-common:jar:1.7.21:compile (version managed from 1.7.21)
[INFO] | - org.jetbrains:annotations:jar:13.0:compile
[INFO] +- org.jetbrains.kotlin:kotlin-reflect:jar:1.7.21:compile
[INFO] | - (org.jetbrains.kotlin:kotlin-stdlib:jar:1.7.21:compile - version managed from 1.7.21; omitted for duplicate)
[INFO] - org.springframework.boot:spring-boot-starter:jar:2.7.7:compile
[INFO] +- org.springframework.boot:spring-boot:jar:2.7.7:compile (version managed from 2.7.7)
[INFO] | +- (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO] | - org.springframework:spring-context:jar:5.3.24:compile (version managed from 5.3.24)
[INFO] | +- org.springframework:spring-aop:jar:5.3.24:compile (version managed from 5.3.24)
[INFO] | | +- (org.springframework:spring-beans:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO] | | - (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO] | +- org.springframework:spring-beans:jar:5.3.24:compile (version managed from 5.3.24)
[INFO] | | - (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO] | +- (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO] | - org.springframework:spring-expression:jar:5.3.24:compile (version managed from 5.3.24)
[INFO] | - (org.springframework:spring-core:jar:5.3.24:compile - version managed from 5.3.24; omitted for duplicate)
[INFO] +- org.springframework.boot:spring-boot-autoconfigure:jar:2.7.7:compile (version managed from 2.7.7)
[INFO] | - (org.springframework.boot:spring-boot:jar:2.7.7:compile - version managed from 2.7.7; omitted for duplicate)
[INFO] +- org.springframework.boot:spring-boot-starter-logging:jar:2.7.7:compile (version managed from 2.7.7)
[INFO] | +- ch.qos.logback:logback-classic:jar:1.2.11:compile (version managed from 1.2.11)
[INFO] | | +- ch.qos.logback:logback-core:jar:1.2.11:compile (version managed from 1.2.11)
[INFO] | | - org.slf4j:slf4j-api:jar:1.7.36:compile (version managed from 1.7.32)
[INFO] | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.2:compile (version managed from 2.17.2)
[INFO] | | +- (org.slf4j:slf4j-api:jar:1.7.36:compile - version managed from 1.7.35; omitted for duplicate)
[INFO] | | - org.apache.logging.log4j:log4j-api:jar:2.17.2:compile (version managed from 2.17.2)
[INFO] | | - org.osgi:org.osgi.core:jar:4.3.1:provided
[INFO] | - org.slf4j:jul-to-slf4j:jar:1.7.36:compile (version managed from 1.7.36)
[INFO] | - (org.slf4j:slf4j-api:jar:1.7.36:compile - version managed from 1.7.36; omitted for duplicate)
[INFO] +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile (version managed from 1.3.5)
[INFO] +- org.springframework:spring-core:jar:5.3.24:compile (version managed from 5.3.24)
[INFO] | - org.springframework:spring-jcl:jar:5.3.24:compile (version managed from 5.3.24)
[INFO] - org.yaml:snakeyaml:jar:1.33:compile (version managed from 1.30)
Specifically, you might notice that slf4j is coming from the `spring-boot-starter-logging` dependency from `spring-boot-starter`
Taking a closer look at the spring boot logging dependency, i'm still not entirely sure where my issue is coming from. i see there are three dependencies here with slf4j, but they don't appear to be duplicates:
[INFO] +- org.springframework.boot:spring-boot-starter-logging:jar:2.7.7:compile (version managed from 2.7.7)
[INFO] | +- ch.qos.logback:logback-classic:jar:1.2.11:compile (version managed from 1.2.11)
[INFO] | | +- ch.qos.logback:logback-core:jar:1.2.11:compile (version managed from 1.2.11)
[INFO] | | - org.slf4j:slf4j-api:jar:1.7.36:compile (version managed from 1.7.32)
[INFO] | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.2:compile (version managed from 2.17.2)
[INFO] | | +- (org.slf4j:slf4j-api:jar:1.7.36:compile - version managed from 1.7.35; omitted for duplicate)
[INFO] | | - org.apache.logging.log4j:log4j-api:jar:2.17.2:compile (version managed from 2.17.2)
[INFO] | | - org.osgi:org.osgi.core:jar:4.3.1:provided
[INFO] | - org.slf4j:jul-to-slf4j:jar:1.7.36:compile (version managed from 1.7.36)
[INFO] | - (org.slf4j:slf4j-api:jar:1.7.36:compile - version managed from 1.7.36; omitted for duplicate)
So what's the issue here? I'm having troubles finding anyone with a similar issue, where most folks are seeing multiple occurrences of slf4j in their dependency tree, though i seem to be struggling to either 1. see where the duplicate is coming from or 2. have a separate issue going on here.
Here is the pom file that's giving me issues:
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>app.demo.server</groupId>
<artifactId>demo-server</artifactId>
<version>${revision}</version>
</parent>
<groupId>app.demo.server.controller</groupId>
<artifactId>controller</artifactId>
<version>${revision}</version>
<packaging>jar</packaging>
<build>
<plugins>
<!-- main module kotlin required plugins begin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>${main.class}</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.version}</version>
<configuration>
<fork>true</fork>
<mainClass>${main.class}</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!--main module kotlin required plugins end-->
</plugins>
</build>
<properties>
<main.class>app.demo.server.driver.demoController</main.class>
</properties>
</project>
Here is the parent pom:
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>app.demo.server</groupId>
<artifactId>demo-server</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<modules>
<module>controller</module>
<module>utilities</module>
</modules>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<jvmTarget>${java.jvm.version}</jvmTarget>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-noarg</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.jvm.version}</source>
<target>${java.jvm.version}</target>
<release>${java.jvm.version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<revision>0.1.0</revision>
<kotlin.version>1.7.21</kotlin.version>
<java.version>11</java.version>
<java.jvm.version>11</java.jvm.version>
<maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
<maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
<maven-assembly-plugin.version>3.4.2</maven-assembly-plugin.version>
<spring.version>2.7.7</spring.version>
<snakeyaml.version>1.33</snakeyaml.version>
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
<logback-classic.version>1.4.5</logback-classic.version>
<logback-core.version>1.4.5</logback-core.version>
</properties>
</project>
I am compiling by running `mvn clean install` then running `java -jar controller/target/controller-${version}.jar`.
I am using kotlin version 7.21 java 11 and mvn 3.6.3
</details>
# 答案1
**得分**: 1
以下是您要翻译的内容:
我相信现在清楚发生了什么:
`spring-boot-maven-plugin` 通常执行以下操作:它获取主要的构件(jar 文件),解压它并创建另一个具有“可执行 jar” 结构的 jar 文件。问题是,在您的情况下,“主要构件” 由于来自父 POM 的 `maven-assembly-plugin` 配置而被覆盖:
```xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
因此,与其重新打包原始的 jar 文件,spring-boot-maven-plugin
重新打包了 maven-assembly-plugin
的结果,因此在“类路径”中会获得重复的类(我认为生成的 jar 文件的大小是预期的两倍)。
您有以下选项:
- 从父 POM 中删除
maven-assembly-plugin
的配置(或将其移到<pluginManagement>
下) - 不要覆盖主要构件,而是创建附加构件,即设置
<appendAssemblyId>true</appendAssemblyId>
- 在控制器 POM 中禁用
maven-assembly-plugin
的配置,通过指定<goal>none</goal>
或<skipAssembly>true</skipAssembly>
,类似于:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>none</goal> </goals>
</execution>
</executions>
</plugin>
或:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<skipAssembly>true</skipAssembly>
</configuration>
</execution>
</executions>
</plugin>
英文:
I believe that is clear what is going on:
spring-boot-maven-plugin
typically does the following: it takes primary artifact (jar file), unpacks it and creates another jar file with "executable jar" structure. The problem is, in your case "primary artifact" gets overridden due to maven-assembly-plugin
configuration coming from parent pom:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
So, instead of repackaging original jar file spring-boot-maven-plugin
repackages result of maven-assembly-plugin
, and thus you are getting duplicate classes in "classpath" (I believe the size of resulting jar is twice more than expected).
You have following options:
- remove
maven-assembly-plugin
configuration from parent pom (or move it under<pluginManagement>
) - do not override primary artifact but create supplemental ones, i.e. set
<appendAssemblyId>true</appendAssemblyId>
- disable
maven-assembly-plugin
configuration in controller pom, via specifying<goal>none</goal>
or<skipAssembly>true</skipAssembly>
something like:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>none</goal> </goals>
</execution>
</executions>
</plugin>
or:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>${maven-assembly-plugin.version}</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<skipAssembly>true</skipAssembly>
</configuration>
</execution>
</executions>
</plugin>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论