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

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

One of Maven‘s major contributions is the way it
deals and manages not only direct dependencies, but also
transitive 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:

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 higher
depth. 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 as
dependency mediation.

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

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

Here is the corresponding dependencies block in POM:

<dependencies>
    <dependency>
        <groupId>directory</groupId>
        <artifactId>apacheds-core</artifactId>
        <version>0.9.3</version>
        <!--implicit dependency to commons-io:
commons-io:1.0-->
    </dependency>
    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-exec</artifactId>
        <version>2.9.7</version>
        <!--implicit dependency to commons-io:commons-io:
1.4-->
    </dependency>
    <dependency>
        <groupId>org.apache.tapestry</groupId>
        <artifactId>tapestry-upload</artifactId>
        <version>5.3.7</version>
        <!--implicit dependency to commons-io:
commons-io:2.0.1-->
    </dependency>
    <dependency>
        <groupId>com.googlecode.grep4j</groupId>
        <artifactId>grep4j</artifactId>
        <version>1.7.5</version>
        <!--implicit dependency to commons-io:commons-io:
2.4-->
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.3</version>
    </dependency>
</dependencies>

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 from
level 1. The dependency tree shown in the following diagram:

All dependencies to
commons-io are of
level 2, and differ on the versions:
0.9.3 (via
apacheds-core),
1.4 (via
camel-exec),
2.0.1 (via
tapestry-upload), and
2.4 (via
grep4j). Unlike a popular belief, the resolution will
not lead 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 before
apacheds-core, its embed version of commons-io
would have been resolved instead of version
0.9.3. This case illustrates the concept of
first found.

Exclusions

Let‘s consider the following example:

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
<exclusion> tag, in order to break the transitive

dependency.

The POM will look similar to the following:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.dbunit</groupId>
        <artifactId>dbunit</artifactId>
        <version>${dbunit.version}</version>
        <scope>test</scope>
        <exclusions>
            <!--Exclude transitive dependency to
JUnit-3.8.2 -->
            <exclusion>
                <artifactId>junit</artifactId>
                <groupId>junit</groupId>
            </exclusion>
            <!--Exclude transitive dependency to
Commons-Collections-3.1-->
            <exclusion>
                <artifactId>commons-collections</artifactId>
                <groupId>commons-collections</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>${commons-collections.version}</version>
        <exclusions>
            <!--Exclude transitive dependency to
JUnit-3.8.1 -->
            <exclusion>
                <artifactId>junit</artifactId>
                <groupId>junit</groupId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

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 on
exclusion 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:

Here are the corresponding POMs:

? For project back, the POM is as follows:

<?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>
    <groupId>com.packt.maven.dependency.optional</groupId>
    <artifactId>back</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>Example of POM which is depended on with 'optional' at true</name>
    <packaging>jar</packaging>
    <!-- no dependency at all -->
</project>

? For project
middle, the POM is as follows:

<?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>
    <groupId>com.packt.maven.dependency.optional</groupId>
    <artifactId>middle</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>Example of POM with an optional dependency</name>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>com.packt.maven.dependency.optional</groupId>
            <artifactId>back</artifactId>
            <version>1.0-SNAPSHOT</version>
            <!-- The dependency to artifact 'back'
is set at optional-->
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

? For project
front, the POM is as follows:

<?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>
    <groupId>com.packt.maven.dependency.optional</groupId>
    <artifactId>front</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>Example of POM with scope import dependencyManagement of two artifacts with a version conflict because of transitive dependencies</name>
    <packaging>jar</packaging>
    <dependencies>
        <!-- Regular dependency ; 'front' depends
on 'middle'-->
        <dependency>
            <groupId>com.packt.maven.dependency.optional</groupId>
            <artifactId>middle</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

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 the
depending project or by the one on which it is depended
on.

读书笔记:

Apache Maven Dependency Management

Copyright ? 2013 Packt Publishing

时间: 2024-08-12 04:48:34

maven之Transitive dependencies(默认树的先序遍历算法处理依赖冲突)的相关文章

算法学习 - 表达树的建立(后缀表达式法),树的先序遍历,中序遍历,后序遍历

表达树就是根据后缀表达式来建立一个二叉树. 这个二叉树的每个叶子节点就是数,真祖先都是操作符. 通过栈来建立的,所以这里也会有很多栈的操作. 树的先序遍历,中序遍历,后序遍历的概念我就不讲了,不会的自行百度,不然也看不懂我的代码. 下面是代码: // // main.cpp // expressionTree // // Created by Alps on 14-7-29. // Copyright (c) 2014年 chen. All rights reserved. // #includ

【编程题目】判断整数序列是不是二元查找树的后序遍历结果,如果是,构建该二元查找树

判断整数序列是不是二元查找树的后序遍历结果题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果.如果是返回 true,否则返回 false.例如输入 5.7.6.9.11.10.8,由于这一整数序列是如下树的后序遍历结果:8/ \6 10/ \ / \5 7 9 11因此返回 true.如果输入 7.4.6.5,没有哪棵树的后序遍历的结果是这个序列,因此返回 false. 做这个题目的时候最开始傻了,想着从前到后根据数字的大小关系判断.后来幡然醒悟,根据后序遍历的特点.序列最后一

树的先序遍历的栈实现

树的先序遍历的栈实现 先把根节点访问了,右子树入栈,去访问左子树. 1 void preorder(tree bt) //先序遍历bt所指的二叉树 2 { 3 tree stack[n]; //栈 4 int top = 0; //栈顶指针 5 tree P; 6 while(bt || top) 7 { 8 while(bt) //非叶结点 9 { 10 cout << bt->data; //访问根 11 stack[++top] = bt->rchild; //右子树压栈 1

微软算法100题09 判断整数序列是不是二元查找树的后序遍历结果

9. 判断整数序列是不是二元查找树的后序遍历结果题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果.如果是返回true,否则返回false.例如输入5.7.6.9.11.10.8,由于这一整数序列是如下树的后序遍历结果:8/ \6 10/ \ / \5 7 9 11因此返回true.如果输入7.4.6.5,没有哪棵树的后序遍历的结果是这个序列,因此返回false. 思路:如果一个数组为BST的后序遍历结果 则最后一个元素必然为该BST的根节点 因为BST的特性是左子树必然全部小

IT公司100题-9-判断整数序列是不是二元查找树的后序遍历结果

问题描述: 输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果. 如果是返回true,否则返回false. 例如输入4, 8, 6, 12, 16, 14, 10,由于这一整数序列是如下树的后序遍历结果: 10/     \6      14/  \    /   \4   8 12    16 因此返回true. 如果输入6, 5, 8, 5, 7 ,则返回false. 分析: 在后续遍历得到的序列中,最后一个元素为树的根结点.根节点元素将数组分为两部分,左边都小于根节点,右边都大

9判断整数序列是不是二元查找树的后序遍历结果

转载请注明出处:http://www.cnblogs.com/wuzetiandaren/p/4252095.html 声明:现大部分文章为寻找问题时在网上相互转载,此博是为自己做个记录记录,方便自己也方便有类似问题的朋友,本文的思想也许有所借鉴,但源码均为本人实现,如有侵权,请发邮件表明文章和原出处地址,我一定在文章中注明.谢谢. 题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果.如果是返回true,否则返回false. 解题思路: 1.输入一个整型数组a,根据该数组创建二

第9题:判断整数序列是不是二元查找树的后序遍历结果

欢迎转载,转载请务必注明出处:http://blog.csdn.net/alading2009/article/details/44872143 第9题:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果.如果是返回true,否则返回false. 例如输入5.7.6.9.11.10.8,由于这一整数序列是如下树的后序遍历结果: 因此返回true. 如果输入7.4.6.5,没有哪棵树的后序遍历的结果是这个序列,因此返回false. 由于二叉查找树的特性(左子树的值小于根节点,右子树的值

6.二元查找树的后序遍历结果

http://zhedahht.blog.163.com/blog/static/25411174200725319627/ 题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果.如果是返回true,否则返回false. 例如输入5.7.6.9.11.10.8,由于这一整数序列是如下树的后序遍历结果: 8       /  \      6    10    / \    / \   5   7   9  11 因此返回true. 如果输入7.4.6.5,没有哪棵树的后序遍历的结

不用堆栈实现树的先序遍历

通常实现树的先序遍历时,我们都需要一个栈来记录位置信息,如果一颗二叉树当中本来就保存了指向父亲的节点,那么我们可以不用堆栈来实现先序遍历. #include<iostream> using namespace std; class node { public: char value; node *parent,*left,*right; node(char v):parent(0),left(0),right(0) { value=v; } }; void first(node *r) { i