maven之Transitive dependencies(默认树的广度优先遍历算法处理依赖冲突)

maven之Transitive dependencies(默认树的广度优先遍历算法处理依赖冲突)



One of Maven's major contributions is the way itdeals and manages not onlydirect dependencies, but alsotransitive ones.
      你项目中的依赖,不管是直接依赖还是间接/传递依赖,maven都能很好的管理。

The concept of transitivity

Dependencies are transitive. This means that if A depends on B and B depends on C, then A depends on both B and C. Theoretically, there is no limit to the depth of dependency. So, if you observe the following diagram of the tree of dependencies,
you will notice that by transitivity, A depends on B, C, D, … until Z:

maven之Transitive dependencies(默认树的广度优先遍历算法处理依赖冲突)_第1张图片

Even worse, we could have added a bit of complexity in mixing different versions of the same artifacts. In this very example with A as root project, B and C are level 1 or direct dependencies, D, E, J, and F are level 2 dependencies, C, G, H, and K are level 3, and so on.

You can imagine that the greater the level of dependencies, the more complex the situation is. The underlying issue of transitivity, you may guess, is when dependencies bear on the same groupId/artifactId but with different versions.

Resolution

Maven carries out the following algorithm to choose between two different versions:
Nearest first: A dependency of lower level has priority over another of the higherdepth. Hence, a direct dependency has priority over a transitive dependency.
First found: At the same level, the first dependency that is found is taken.

This algorithm is known asdependency mediation.

其实就是数据结构中,树的 广度优先 遍历算法。

Let's consider an example. The following diagram shows a dependency tree:

maven之Transitive dependencies(默认树的广度优先遍历算法处理依赖冲突)_第2张图片


Here is the corresponding dependencies block in POM:



    
        directory
        apacheds-core
        0.9.3
        
    
    
        org.apache.camel
        camel-exec
        2.9.7
        
    
    
        org.apache.tapestry
        tapestry-upload
        5.3.7
        
    
    
        com.googlecode.grep4j
        grep4j
        1.7.5
        
    
    
        commons-io
        commons-io
        2.3
    

The commons-io-2.3 dependency is a dependency of level 1. So, even though it is declared after other artifacts and their transitive dependencies, then the dependency mediation will resolve commons-io to version 2.3. This case illustrates the concept of  nearest first.

Now let's compare to a POM for which commons-io-2.3 has been deleted fromlevel 1. The dependency tree shown in the following diagram:

maven之Transitive dependencies(默认树的广度优先遍历算法处理依赖冲突)_第3张图片


All dependencies tocommons-ioare oflevel 2, and differ on the versions:0.9.3 (viaapacheds-core),1.4(via camel-exec),2.0.1 (viatapestry-upload), and2.4 (viagrep4j). Unlike a popular belief, the resolution willnotlead to take the greatest
version number (that is,
2.4), but the first transitive version that appears in the dependency tree, in other terms 0.9.3.


Had another dependency been declared beforeapacheds-core, its embed version of commons-iowould have been resolved instead of version0.9.3. This case illustrates the concept offirst found.


Exclusions

Let's consider the following example:


maven之Transitive dependencies(默认树的广度优先遍历算法处理依赖冲突)_第4张图片



Our project needs JUnit-4.11, as well as DBUnit-2.4.9 and commonscollections-2.1. But the two latter depend on other versions of JUnit, respectively 2.3.0 and 3.2. Moreover, commons-collections depends on JUnit-3.8.1. Therefore, on building the project with goal test, we may encounter strange behaviors.
In this situation, you have to use an tag, in order to break the transitive
dependency
.
The POM will look similar to the following:


    
        junit
        junit
        ${junit.version}
        test
    
    
        org.dbunit
        dbunit
        ${dbunit.version}
        test
        
            
            
                junit
                junit
            
            
            
                commons-collections
                commons-collections
            
        
    
    
        commons-collections
        commons-collections
        ${commons-collections.version}
        
            
            
                junit
                junit
            
        
    


Most of the time, you will choose to exclude a transitive dependency for one of the
following reasons:

Conflicts between versions of the same artifact of your project, such as preceding version.
Conflicts between artifacts of your project and artifacts from the platform of deployment, such as Tomcat or another server. For instance, if your project depends on wsdl4j-1.5.3 and is deployed on JBoss AS 7.1.1, then a conflict may appear with JBoss's dependency to wsdl4j-1.6.2.
• In some cases, you do not want some of your dependencies to be exported within the archive you build (even though in this case, using a play on the dependency scope should be more elegant). The opposite case (when you need use your own dependencies and ignore the similar artifacts bundled with the server) will be exposed in Chapter 6, Release and Distribute.


Optional dependencies


The previous mechanism, based onexclusion tag, is in charge of the depending project to exclude unwanted dependencies.
Another mean exists to exclude transitive dependencies. This time, the charge lies on the project on which it is depended on.Maven provides the optional tag that takes a boolean value (true/false).
Let's consider the following example of dependencies:

maven之Transitive dependencies(默认树的广度优先遍历算法处理依赖冲突)_第5张图片

Here are the corresponding POMs:
• For project back, the POM is as follows:



    4.0.0
    com.packt.maven.dependency.optional
    back
    1.0-SNAPSHOT
    Example of POM which is depended on with 'optional' at true
    jar
    


• For project middle, the POM is as follows:



    4.0.0
    com.packt.maven.dependency.optional
    middle
    1.0-SNAPSHOT
    Example of POM with an optional dependency
    jar
    
        
            com.packt.maven.dependency.optional
            back
            1.0-SNAPSHOT
            
            true
        
    


• For project front, the POM is as follows:



    4.0.0
    com.packt.maven.dependency.optional
    front
    1.0-SNAPSHOT
    Example of POM with scope import dependencyManagement of two artifacts with a version conflict because of transitive dependencies
    jar
    
        
        
            com.packt.maven.dependency.optional
            middle
            1.0-SNAPSHOT
        
    


Now, we will see how to display the dependency trees. For middle, the tree is not different from what it would be, had the optional tag been set at false:
[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ middle ---
[INFO] com.packt.maven.dependency.optional:middle:jar:1.0-SNAPSHOT
[INFO] \- com.packt.maven.dependency.optional:back:jar:1.0-
SNAPSHOT:compile

But for front, we get the following output:
[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ front ---
[INFO] com.packt.maven.dependency.optional:front:jar:1.0-SNAPSHOT
[INFO] \- com.packt.maven.dependency.optional:middle:jar:1.0-
SNAPSHOT:compile


In other terms, middle has prevented its dependency to back to propagate transitively to other projects that depend on middle (among which front; but middle has no idea of front).
Had we removed the optional tag, we would have got that other trace:

[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ front ---
[INFO] com.packt.maven.dependency.optional:front:jar:1.0-SNAPSHOT
[INFO] \- com.packt.maven.dependency.optional:middle:jar:1.0-
SNAPSHOT:compile
[INFO] \- com.packt.maven.dependency.optional:back:jar:1.0-
SNAPSHOT:compile


As a conclusion, exclusions and optional allow to break the chain of transitivity. This may be driven either by thedependingproject or by the one on which it is depended on.



读书笔记:
Apache Maven Dependency Management
Copyright © 2013 Packt Publishing

你可能感兴趣的:(maven,maven学习)