Analysis and Solution of Maven 3-Maven Dependent Version Conflict

Keywords: Python Maven xml Zookeeper Java

Extracted from: https://www.cnblogs.com/aspirant/p/8532962.html

Give an example
A relies on B and C, while B relies on X and Y, while C relies on X and M. A will introduce X, Y and M dependency packages besides B and C dependency packages. (Generally, Maven can control transfer dependencies by < scope> and so on.)
Here's a special note: B and C depend on X at the same time. Assuming that B depends on version 1.0 of X and C depends on version 2.0 of X, does A depend on version 1.0 or version 2.0 of X?
This depends on the loading order of Classloader. Suppose Classloader loads X_1.0 first, and it will not load X_2.0 again. If A just wants to use X_2.0, the blood case will happen unexpectedly.

 

For example, A relies on version 2.0 C, B relies on version 3.0 C. In your pom, you depend on both A and B, and there is conflict. At this point, you have to decide which version can make A and B work at the same time (if possible), and then exclude the other one. I usually exclude lower versions. -

 

<dependencies>  
        <dependency>  
            <groupId>A</groupId>  
            <artifactId>A</artifactId>  
            <version>xxx</version>  
            <exclusions>  
                <exclusion>  
                    <groupId>C</groupId>  
                    <artifactId>C</artifactId>  
                </exclusion>  
            </exclusions>  
        </dependency>  
        <dependency>  
            <groupId>B</groupId>  
            <artifactId>B</artifactId>              
        </dependency>  
</dependencies>

  

Understanding package dependency is one of Maven's core functions. Here are six questions: how to introduce jar package; how to parse jar package dependency; how to generate package conflict; how to resolve package conflict; what problem to solve by dependency management; what is dependency scope; and how to use best practices of package dependency.

How to introduce jar package

In code development, if you need to use the class libraries provided by third-party jar packages, you need to add the jar package dependency to pom.xml. For example, use zookeeper client

<dependencies>
  <!-- https://mvnrepository.com/artifact/org.apache.hadoop/zookeeper -->
  <dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>zookeeper</artifactId>
      <version>3.3.1</version>
  </dependency>
</dependencies>

  

How Maven Resolves jar Packet Dependency-Transfer Dependency

As mentioned above, zookeeper jar package dependency is introduced into pom.xml. When Maven parses the dependency, not only zookeeper, but also jar packages of internal dependency of zookeeper and jar packages of internal dependency of jar packages of internal dependency of zookeeper will be introduced. The dependency relationship will be passed on until no dependency exists. Dependence.
How does the package conflict arise?

For example, suppose that A - > B - > C - > D1, E - > F - > D2, D1 and D2 are different versions of D, respectively.
If A and E are introduced into the pom.xml file, according to Maven's transfer dependency principle, the actual Jar packages that need to be introduced in the project will be: A B C D1 and E F D2, so D1 and D2 will cause package conflicts.

How to Resolve Package Conflict

When Maven parses the pom.xml file, only one jar package is reserved for the same jar package, which effectively avoids the instability of the project caused by the introduction of two jar packages.
Maven default processing strategy

Shortest Path First
When Maven faces D1 and D2, it defaults to choose the shortest path jar package, D2. E - > F - > D2 is 1 shorter than A - > B - > C - > D1.
First declare priority
If the path is the same, for example: A - > B - > C1, E - > F - > C2, the length of both dependent paths is 2, then choose the first declaration.
Remove dependencies

If we don't want to introduce D1 through A - > B - > D1, then we exclude D1 when declaring the introduction of A, which also avoids package conflicts.
For example, exclude zookeeper's jline dependencies from exclusions Tags

  

<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.3.1</version>
    <exclusions>
        <exclusion>
            <groupId>jline</groupId>
            <artifactId>jline</artifactId>
        </exclusion>
    </exclusions>
</dependency>

  

 

Detecting Packet Conflict Tool

mvn dependency:help

mvn dependency:analyze

mvn dependency:tree

mvn dependency:tree -Dverbose

Detailed reference: mvn dependency
mvn dependency:tree

What are the problems to be solved by relying on Management

When there are multiple modules in the same project, and multiple modules are required to use the same version of a jar package, in order to facilitate the unification of version number and upgrade version number, it is necessary to extract a father module to manage the version of the jar package that the sub-modules depend on.
For example, there are two modules, projectA and projectB, whose dependencies are as follows:


projectA:

<project>
  ...
  <dependencies>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-a</artifactId>
      <version>1.0</version>
      <exclusions>
        <exclusion>
          <groupId>group-c</groupId>
          <artifactId>excluded-artifact</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
      <version>1.0</version>
      <type>bar</type>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>

  

projectB:

<project>
  ...
  <dependencies>
    <dependency>
      <groupId>group-c</groupId>
      <artifactId>artifact-b</artifactId>
      <version>1.0</version>
      <type>war</type>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
      <version>1.0</version>
      <type>bar</type>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>

  

Project A and project B rely on group-a/artifact-b/1.0 to extract common dependencies and generate parent. Parent dependencies are as follows:

<project>
  ...
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>group-a</groupId>
        <artifactId>artifact-b</artifactId>
        <version>1.0</version>
        <type>bar</type>
        <scope>runtime</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

  

 

Neither project A nor project B need to specify the version information of group-a/artifact-b, and only need to specify within the parent to upgrade version information in the future.

projectA:

<project>
  ...
<parent>
<groupId>group-a</groupId>
        <artifactId>artifact-b</artifactId>
        <version>1.0</version>
</parent>
  <dependencies>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-a</artifactId>
      <version>1.0</version>
      <exclusions>
        <exclusion>
          <groupId>group-c</groupId>
          <artifactId>excluded-artifact</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
    </dependency>
  </dependencies>
</project>

  

projectB:

<project>
  ...
  <dependencies>
    <dependency>
      <groupId>group-c</groupId>
      <artifactId>artifact-b</artifactId>
      <version>1.0</version>
      <type>war</type>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>group-a</groupId>
      <artifactId>artifact-b</artifactId>
    </dependency>
  </dependencies>
</project>

  

 

Scope of dependency

If the < scope > attribute is not displayed, the default is < scope > compile </scope >.
What attributes does scope have: compile, provided, runtime, test, system, etc.
Detailed reference: Scope of dependency

Best Practices

(1) The jar package used by the source code in the project must display references in pom.xml.
(2) check package conflicts frequently to see if they need to be handled.
(3) When using multiple modules, the parent must use the package management module to standardize the Jar package version, instead of directly introducing dependencies into the package dependency module. Dependency Management vs Dependencies

 

Second question:
First axe: Find out where the ghost of transmission dependency comes from?

dependency:tree is to illuminate the demon, pom.xml to illuminate it, all transitive dependencies will be nowhere to hide, and will be displayed in a hierarchical tree way, very intuitive.

The following is an output after dependency:tree execution:

 

Quote

[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ euler-foundation ---
[INFO] com.hsit:euler-foundation:jar:0.9.0.1-SNAPSHOT
[INFO] +- com.rop:rop:jar:1.0.1:compile
[INFO] |  +- org.slf4j:slf4j-api:jar:1.7.5:compile
[INFO] |  +- org.slf4j:slf4j-log4j12:jar:1.7.5:compile
[INFO] |  +- log4j:log4j:jar:1.2.16:compile
[INFO] |  +- commons-lang:commons-lang:jar:2.6:compile
[INFO] |  +- commons-codec:commons-codec:jar:1.6:compile
[INFO] |  +- javax.validation:validation-api:jar:1.0.0.GA:compile
[INFO] |  +- org.hibernate:hibernate-validator:jar:4.2.0.Final:compile
[INFO] |  +- org.codehaus.jackson:jackson-core-asl:jar:1.9.5:compile
[INFO] |  +- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.5:compile
[INFO] |  +- org.codehaus.jackson:jackson-jaxrs:jar:1.9.5:compile
[INFO] |  +- org.codehaus.jackson:jackson-xc:jar:1.9.5:compile
[INFO] |  \- com.fasterxml.jackson.dataformat:jackson-dataformat-xml:jar:2.2.3:compile
[INFO] |     +- com.fasterxml.jackson.core:jackson-core:jar:2.2.3:compile
[INFO] |     +- com.fasterxml.jackson.core:jackson-annotations:jar:2.2.3:compile
[INFO] |     +- com.fasterxml.jackson.core:jackson-databind:jar:2.2.3:compile
[INFO] |     +- com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:2.2.3:compile
[INFO] |     \- org.codehaus.woodstox:stax2-api:jar:3.1.1:compile
[INFO] |        \- javax.xml.stream:stax-api:jar:1.0-2:compile

  

When I bragged about dependency:tree, I used "nowhere to hide." In fact, sometimes you will find that dependency:tree simply does not always show all the delivery dependencies. But if you really want to see everything, you have to add a - Dverbose parameter, which must be the most complete.
It's all over, but it shows too many things, dizziness. Is there a good way? Of course, with Dincludes or Dexcludes saying you like it or hate it, dependency:tree will filter it out for you:
Quote

Dincludes=org.springframework:spring-tx

  

The filter string is filtered by groupId:artifactId:version, which can not be written completely, such as:

mvn dependency:tree -Dverbose -Dincludes=asm:asm  

  

The asm dependency package analysis information will come out:

[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ ridge-test ---
[INFO] com.ridge:ridge-test:jar:1.0.2-SNAPSHOT
[INFO] +- asm:asm:jar:3.2:compile
[INFO] \- org.unitils:unitils-dbmaintainer:jar:3.3:compile
[INFO]    \- org.hibernate:hibernate:jar:3.2.5.ga:compile
[INFO]       +- cglib:cglib:jar:2.1_3:compile
[INFO]       |  \- (asm:asm:jar:1.5.3:compile - omitted for conflict with 3.2)
[INFO]       \- (asm:asm:jar:1.5.3:compile - omitted for conflict with 3.2)
[INFO] ------------------------------------------------------------------------

  

 

There is a direct dependence on ASM (asm:asm:jar:3.2) and a transitive entry dependence (asm:asm:jar:1.5.3)

Second axe: Cut out unwanted transfer dependencies

Let's assume that we don't want asm:asm:jar:1.5.3. According to the analysis, we know that it was introduced through org.unitils:unitils-dbmaintainer:jar:3.3. Find this dependency in pom.xml and make other adjustments:

<dependency>  
        <groupId>org.unitils</groupId>  
        <artifactId>unitils-dbmaintainer</artifactId>  
        <version>${unitils.version}</version>  
        <exclusions>  
            <exclusion>  
                <artifactId>dbunit</artifactId>  
                <groupId>org.dbunit</groupId>  
            </exclusion>  
            <!-- This is what we're going to add. -->  
            <exclusion>  
                <artifactId>asm</artifactId>  
                <groupId>asm</groupId>  
            </exclusion>  
        </exclusions>  
    </dependency>

  

Once again, you can see that the delivery dependency is gone:

[INFO]  
    [INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ ridge-test ---  
    [INFO] com.ridge:ridge-test:jar:1.0.2-SNAPSHOT  
    [INFO] \- asm:asm:jar:3.2:compile  
    [INFO] ------------------------------------------------------------------------  
    [INFO] BUILD SUCCESS

  

Third axe: View JAR packages from runtime class sources

Sometimes, you think it's solved, but it's still a package collision (typically java.lang.ClassNotFoundException or Method Incompatibility, etc.). You can set a breakpoint at which you can view the JAR package from which Class comes by using the following tool class I did:

package com.ridge.util;  
      
    import java.io.File;  
    import java.net.MalformedURLException;  
    import java.net.URL;  
    import java.security.CodeSource;  
    import java.security.ProtectionDomain;  
      
    /** 
     * @author : chenxh 
     * @date: 13-10-31 
     */  
    public class ClassLocationUtils {  
      
        /** 
         * Get all the paths of the class 
         * @param cls 
         * @return 
         */  
        public static String where(final Class cls) {  
            if (cls == null)throw new IllegalArgumentException("null input: cls");  
            URL result = null;  
            final String clsAsResource = cls.getName().replace('.', '/').concat(".class");  
            final ProtectionDomain pd = cls.getProtectionDomain();  
            if (pd != null) {  
                final CodeSource cs = pd.getCodeSource();  
                if (cs != null) result = cs.getLocation();  
                if (result != null) {  
                    if ("file".equals(result.getProtocol())) {  
                        try {  
                            if (result.toExternalForm().endsWith(".jar") ||  
                                    result.toExternalForm().endsWith(".zip"))  
                                result = new URL("jar:".concat(result.toExternalForm())  
                                        .concat("!/").concat(clsAsResource));  
                            else if (new File(result.getFile()).isDirectory())  
                                result = new URL(result, clsAsResource);  
                        }  
                        catch (MalformedURLException ignore) {}  
                    }  
                }  
            }  
            if (result == null) {  
                final ClassLoader clsLoader = cls.getClassLoader();  
                result = clsLoader != null ?  
                        clsLoader.getResource(clsAsResource) :  
                        ClassLoader.getSystemResource(clsAsResource);  
            }  
            return result.toString();  
        }  
      
    }

  

Write a test randomly, set the breakpoint, and press alt+F8 to execute the code dynamically (intelij idea) at the breakpoint. Suppose we enter:

ClassLocationUtils.where(org.objectweb.asm.ClassVisitor.class)  

  

The JAR corresponding to the class can be found immediately:

This is the JAR package corresponding to the org.objectweb.asm.ClassVisitor class at runtime. If this version of the JAR package is not what you expect, it is caused by your IDE cache. It is recommended that you re-import the maven list, as follows (idea):

Reimport, the IDE forces a reanalysis and loading of dependency packages based on the new pom.xml settings to get the same dependencies as the pom.xml settings. (This step is very important, oh, the current project group pom.xml is the same, but some people can run, some people can not run, commonly known as personality problems, in fact, are caused by the IDE cache.
Idea clears the cache. In order to improve the efficiency, reimport is not recommended to restart the project. Idea's own function is recommended. File - > Invalidate Caches function completes the clearance of idea cache directly.

 

Third, another problem, log conflict: The problems in the project are as follows:

Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.apache.log4j.Log4jLoggerFactory

  

After online search, Daniel pointed out that:

log4j-over-slf4j.jar and slf4j-log4j12.jar will make this error under the same classpath.

Solutions:
Exclude slf4j-log4j12.jar from relevant jars

But looking at the pom file in the maven project, I didn't configure the dependency of this jar. I guess it's Maven that loads the dependency packages introduced by other jars.

Open the pom.xml file and view the dependency hierarchy of the jar package in Dependency Hierarchy.

 

Enter log4j in the filter bar, and the dependency structure of log4j related packages appears on the right, while the list of all dependency packages of pom.xml appears on the left.

Select the jar package of slf4j under zookeeper directly on the right, right-click Exclude, and save pom.xml. This way, the jar package of slf4j will not be loaded when zookeeper's jar package is loaded.

The corresponding dependency file after modification is as follows:

<dependency>  
    <groupId>org.apache.zookeeper</groupId>  
    <artifactId>zookeeper</artifactId>  
    <version>3.4.6</version>  
    <exclusions>  
        <exclusion>  
            <artifactId>slf4j-log4j12</artifactId>  
            <groupId>org.slf4j</groupId>  
        </exclusion>  
    </exclusions>  
</dependency>

  

In this way, we can quickly find the corresponding jar through filter filtering, and know its dependencies, and quickly solve the jar package conflict problem in the project. -

Reference resources: Ultimate Solution to Maven Class Package Conflict

Reference resources: Using maven tools to solve jar package conflicts or duplicate loading problems

Reference resources: Maven's Principle of Resolving jar Packet Conflict

Posted by xiledweb on Sun, 21 Jul 2019 00:53:52 -0700