maven resolve jar conflicts

How did you say that... 

quote
If you love him, please let him use Maven, because there is heaven, if you hate him, please let him use Maven, because there is hell.



Maven is "startling every step of the way" for beginners, because it is all-encompassing and profound, because when you first arrive, you are as confused as a strange visitor entering the forest. 

Maven is the "Recipe of True Love" for veterans, because it is omnipotent, as sharp as a blade, and using Maven for development is like drinking fine wine and delighting a beautiful woman. 

One of the most painful things about Maven for newbies is the conflict between packages. Due to Maven's dependency transitivity, when you introduce a dependency class, the dependency classes behind it also come along like a crucian carp across the river. 

For example, a chestnut 

A depends on B and C, and B depends on X, Y, and C depends on X, M, then A will introduce the dependencies of X, Y, and M in addition to the dependencies of B and C. (In general, Maven can control transitive dependencies in several ways such as <scope>). 

There is a need to pay special attention here, that is, B and C depend on X at the same time. Suppose B depends on the 1.0 version of X, and C depends on the 2.0 version of X. Does A depend on the 1.0 or 2.0 version of X? 

This depends on the loading order of the Classloader. Assuming that the Classloader loads X_1.0 first, it will not load X_2.0 again. If A just wants to use X_2.0, the murder case happens unexpectedly. 

The first of the three axes that I often swing 

: Where is the ghost of finding transitive dependencies? 

Dependency:tree is a ghost photo, pom.xml uses it to photo, all transitive dependencies will have nowhere to hide, and will be displayed in a hierarchical tree, which is very intuitive. 

The following is an output after executing dependency:tree: 

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 was bragging about dependency:tree, I used "nowhere to hide". In fact, sometimes you will find that simply using dependency:tree often doesn't see all transitive dependencies. But if you really want to see everything, you must add a -Dverbose parameter, then it must be the most complete. 

    Everything is complete, but there are too many things displayed, and I feel dizzy. Is there a good way? Of course, add Dincludes or Dexcludes to say what you like or hate, and dependency:tree will filter them out for you: 

quote
Dincludes=org.springframework:spring-tx


   The filter string is filtered using the method of groupId:artifactId:version, you can leave it out, for example: 

Java code   Favorite code
  1. mvn dependency:tree -Dverbose -Dincludes=asm:asm  


   The analysis information of the asm dependency package will come out: 

quote

[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] ------------------------------------------------------------------------ 


   Dependency on asm There is a direct dependency (asm:asm:jar:3.2) and a transitive dependency (asm:asm:jar:1.5.3)     Second

axe: Cut out unwanted transitive dependencies 

Above, assuming we don't want asm:asm:jar:1.5.3 to appear, according to the analysis, we know that it is introduced via org.unitils:unitils-dbmaintainer:jar:3.3, then find this dependency in pom.xml and do Other adjustments: 

Xml code   Favorite code
  1. <dependency>  
  2.     <groupId>org.unitils</groupId>  
  3.     <artifactId>unitils-dbmaintainer</artifactId>  
  4.     <version>${unitils.version}</version>  
  5.     <exclusions>  
  6.         <exclusion>  
  7.             <artifactId>dbunit</artifactId>  
  8.             <groupId>org.dbunit</groupId>  
  9.         </exclusion>  
  10.         <!-- This is the snippet we're going to add -->  
  11.         <exclusion>  
  12.             <artifactId>asm</artifactId>  
  13.             <groupId>asm</groupId>  
  14.         </exclusion>  
  15.     </exclusions>  
  16. </dependency>  



   Analyzing it again, you can see that the transitive dependencies are gone: 

Xml code   Favorite code
  1. [INFO]  
  2. [INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ ridge-test ---  
  3. [INFO] com.ridge:ridge-test:jar:1.0.2-SNAPSHOT  
  4. [INFO] \- asm:asm:jar:3.2:compile  
  5. [INFO] ------------------------------------------------------------------------  
  6. [INFO] BUILD SUCCESS  



The third axe: Check the JAR package of the runtime class source 

Sometimes, you think it is solved, but it still reports a class package conflict (typical symptoms are exceptions such as java.lang.ClassNotFoundException or Method incompatibility), then you can set a break Click the breakpoint to view the JAR package from which the Class comes from through the following tool class I made: 

Java code   Favorite code
  1. package com.ridge.util;  
  2.   
  3. import java.io.File;  
  4. import java.net.MalformedURLException;  
  5. import java.net.URL;  
  6. import java.security.CodeSource;  
  7. import java.security.ProtectionDomain;  
  8.   
  9. /** 
  10.  * @author : chenxh 
  11.  * @date: 13-10-31 
  12.  */  
  13. public class ClassLocationUtils {  
  14.   
  15.     /** 
  16.      * Get all the paths of the class 
  17.      * @param cls 
  18.      * @return 
  19.      */  
  20.     public static String where(final Class cls) {  
  21.         if (cls == null)throw new IllegalArgumentException("null input: cls");  
  22.         URL result = null;  
  23.         final String clsAsResource = cls.getName().replace('.', '/').concat(".class");  
  24.         final ProtectionDomain pd = cls.getProtectionDomain();  
  25.         if (pd != null) {  
  26.             final CodeSource cs = pd.getCodeSource ();  
  27.             if (cs != null) result = cs.getLocation();  
  28.             if (result != null) {  
  29.                 if ("file".equals(result.getProtocol())) {  
  30.                     try {  
  31.                         if (result.toExternalForm().endsWith(".jar") ||  
  32.                                 result.toExternalForm().endsWith(".zip"))  
  33.                             result = new URL("jar:".concat(result.toExternalForm())  
  34.                                     .concat("!/").concat(clsAsResource));  
  35.                         else if (new File(result.getFile()).isDirectory())  
  36.                             result = new URL(result, clsAsResource);  
  37.                     }  
  38.                     catch (MalformedURLException ignore) {}  
  39.                 }  
  40.             }  
  41.         }  
  42.         if (result == null) {  
  43.             final ClassLoader clsLoader = cls.getClassLoader();  
  44.             result = clsLoader != null ?  
  45.                     clsLoader.getResource(clsAsResource) :  
  46.                     ClassLoader.getSystemResource(clsAsResource);  
  47.         }  
  48.         return result.toString();  
  49.     }  
  50.   
  51. }  



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

Java code   Favorite code
  1. ClassLocationUtils.where(org.objectweb.asm.ClassVisitor.class)  


You can immediately find out the JAR corresponding to the class: 


this is the JAR package corresponding to the org.objectweb.asm.ClassVisitor class at runtime. If the version of the JAR package is not what you expected, it means that your IDE cache is caused. It is recommended that you reimport the maven list, as shown below (idea): 

 

Reimport, the IDE will force the re-analysis and loading of the dependent class package according to the new pom.xml settings to obtain the same dependencies as the pom.xml settings. (This step is very important, often the project team pom.xml is the same, but some people can run it, some people can't run it, commonly known as the character problem, which is actually caused by the IDE's cache  ) 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324816745&siteId=291194637