`
leonzhx
  • 浏览: 796550 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

关于 Maven的传递依赖的理解

阅读更多

今天又回顾了一下 《Maven in Action》的读书笔记第五章 : http://seanzhou.iteye.com/admin/blogs/1290558。再次看到这张Maven 传递依赖的表格:

 

 

假设 A 依赖 B B 依赖 C ,我们称 A B 是第一直接依赖, B C 是第二直接依赖, A C 是传递依赖。下表显示了三者的关系:

第一          第二

compile

test

provided

runtime

compile

compile

-

-

runtime

test

test

-

-

test

provided

provided

-

provided

provided

runtime

runtime

-

-

runtime

 

 

 

当时就对这张表格有点一知半解,向同事和老鸟请教,大都无法对其进行解释,甚至在某次技术会议现场遇到《Maven in action》的作者Juven,向其讨教,他也一时无法解释。

    如今在Maven项目上实践了这么久,又对这张表有了新的理解。首先,我觉得所谓A对B有依赖,是指A对B的artifact有依赖,也就是依赖的是B的binary。其次依赖范围决定了A对B在什么时间段具有依赖。一共有三种时间段:a)在编译源代码时 b) 在编译测试代码及运行测试用例时  c)  在运行时

    四种依赖范围分别表示了在不同的时间段具有依赖:

1)compile: a) & b) & c)

2)test : b)

3)provided: a) & b)

4) runtime: c)

 

可见test与runtime是最弱的依赖,其次是provided,然后再是compile。下面我们来看传递依赖,我们换个角度,先不从依赖范围来看传递依赖,而是从依赖的时间段来看传递依赖。

 

                           第二

第一

            a)                        b)                        c)           
              a)             -             -             -
              b)             -             -             b)
              c)             -             -             c)

 

解释一下,第一列表示A对B的依赖时间段,第二列表示B对C的依赖时间段。首先,如果 A对B是在编译时依赖,那A对C一定没有依赖,因为编译源代码时只需要B的binary即可通过编译,B的依赖都不会需要。所以第二行都为没有依赖。其次如果B对C为编译时依赖,因为之前说过A对B的依赖都是指A对B的binary的依赖,B无需重新编译源代码,所以第二列都为没有依赖。同样的如果B对C为编译和运行测试代码时依赖,因为无论A只对B的binary有依赖(主代码),与B的测试代码没有关系,所以第三列也都为没有依赖。所以现在只剩下表格中最后一列的最后两格需要解释了:

    1. A对B在编译及执行测试代码时有依赖,而B对C在主代码运行时有依赖

     对编译A的测试代码来说,A只需要B的binary,一定对C没有依赖,但对执行A的测试代码来说,执行时需要调用到B代码,而这时对B来说这是它的主代码的运行时,它对C是有依赖的。所以A对C也就有了依赖(A运行测试代码时对C有依赖)

     2. A对B主代码运行时有依赖,而B对C在主代码运行时有依赖

     同理,在运行A的主代码时,A需要调用B的主代码,而这时也是B的主代码的运行时,所以B需要调用C。所以A对C也有了依赖(A运行主代码时对C有依赖)。

 

    下面我们来看依赖范围,我们将依赖范围的这张传递依赖表转换成,依赖时间段的传递依赖表:

第一         第二

a)&b)&c) 

b)

a)&b)

c)

     a)&b)&c)

b)&c)

-

b)&c) 

         b)

b)

-

-

b)

      a)&b)

b)

-

b)

         c)

c)

-

-

c)

 

我们来对比一下原表:

    1)  compile + compile --> compile, 而我们这里推出的是 b) & c),因为没有一个特定范围对应 b) & c)所以用compile来代替是合理的。

    2) compile + provided -->无依赖也是合理的,但有一点必须注意,provided这个范围是比较特殊的,它并不是说在运行时没有依赖,而是说运行时的依赖会由运行环境提供,这点对传递依赖很重要。对于compile + provided的情况,我们必须保证在A的测试环境中C已经存在了 ,不然是会有问题的。因为B假定C在B的运行时已经存在了。比如说 C是servelt api,在deploy B到一个servlet container中时,C会由container提供,但在A的测试和运行环境中,必须也保证C已经存在(特别是A的测试环境)不然就有可能出现runtime exception。

    3) compile + runtime --> runtime, 这点有些不合理,这会导致,在A的测试代码运行时,它调用B,而B需要调用C,这时会出现runtime excpetion。(所以我推导出的是 b) & c) ) 我持着怀疑的态度特地在Maven 3.03上验证了一下,确实compile + runtime --> runtime。所以这里我们就要特别小心了,如果A的测试代码运行时,有可能需要间接调用到C,你需要显示声明C为A的compile依赖。 (因为默认是runtime依赖,加上test依赖就是compile依赖)

    4)provided + compile --> provided , 这个是很有问题的,首先, A对C在编译时肯定是不需要的(有B的binary就足够编译了)。所以我们推出的是c),也就是只在测试时需要(运行测试代码时需要)。但有一点我们需要多思考一下的,A对B是provided依赖,表明在A的主代码运行时B已经被环境提供了,A在运行时是依赖B的,而B对C是compile依赖,也就是B在运行时对C有依赖,也就是说A在运行时其实是间接依赖C的,而如果A的运行时环境提供了B,没理由不提供B的运行时依赖C, 所以这里最后得到的是provided(因为test无法表示运行时的依赖性)。

    5)provided + provided --> provided,虽然我推导出的是无依赖,但由于provided的特殊性,provided并不能简单用 a) & b)来表示,其实在运行时是有依赖的,只是依赖由环境提供。首先A对C肯定在编译时是没有依赖的,但在运行测试代码时是有依赖的,因为B对C是provided依赖,也就是说C由B的运行环境提供,所以在A的测试环境需要提供C。和上节同样的道理,A的运行环境提供了B,没道理不提供B的运行时依赖C(B项目也假定了在其运行时会由环境提供C)所以是provided依赖。

    6) provided + runtime --> provided,这点其实得provided + compile --> provided同理。

 

 

   OK, 这样原先的大部分疑惑都已经解决了,回过头来看一下,主要是provided这个范围比较搞,它并不表示在运行时没有依赖,只是表明依赖会由运行时环境提供,但问题是如何预先知道项目的运行时环境,项目的运行时环境是会变化的。因为如果A在运行时依赖B,A的运行时环境就成了B的运行时环境可能会与原先单独运行B的环境不同。感觉Maven的这一设计有些许问题。

 

3
1
分享到:
评论
5 楼 心存高远 2018-01-17  
谢谢作者分享,刚好看到这里不太明白,现在茅塞顿开。不过runtime依赖范围其实对测试classpaths也是有效的。官网原文:runtime,This scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath.
4 楼 sxlkk 2015-12-22  
851228082 写道
甚至在某次技术会议现场遇到《Maven in action》的作者Juven,向其讨教,他也一时无法解释。

作者自己都不知道?!!!  我也是醉了

哈哈
3 楼 851228082 2015-10-27  
甚至在某次技术会议现场遇到《Maven in action》的作者Juven,向其讨教,他也一时无法解释。

作者自己都不知道?!!!  我也是醉了
2 楼 leonzhx 2013-11-14  
谢谢你提供的链接,刚刚看了一下,你好像说反了,非但compile+provided ==>无依赖,连provided + provided ==>无依赖了。
1 楼 无红墙 2013-11-13  
这张依赖传递的表跟maven官网 http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope 上的好像不一样。

compile+provided => provided,而非无依赖

相关推荐

    Maven传递依赖和依赖的规则.docx

    **一、Maven传递依赖** 传递依赖是指在Maven项目中,当一个项目A依赖于另一个项目B,而项目B又依赖于项目C时,项目A间接地依赖于项目C。这种关系使得A可以通过B来间接获取到C的类和资源。但是,并非所有的传递依赖...

    Maven 依赖详细理解.pdf

    Maven是Apache软件基金会的一个项目,用于项目对象模型(Project Object Model)的管理和构建自动化。...通过合理地使用POM文件、依赖范围、依赖调解和传递性依赖等机制,我们可以更好地构建和维护我们的Maven项目。

    Maven的依赖验证项目

    Maven的依赖机制遵循“传递性”原则,这意味着如果你的项目依赖A,而A又依赖B,那么Maven会自动将B也引入到你的项目中。但是,这可能导致版本冲突,因此Maven提供了`exclusions`标签来排除不需要的依赖。 在进行...

    maven依赖小例子

    Maven依赖管理遵循“传递性”原则,即如果你的项目依赖A库,而A库又依赖B库,Maven会自动将B库也一并引入。这大大简化了项目的构建过程,但同时也可能导致依赖冲突,需要通过排除机制或调整依赖版本来解决。 在`...

    ​实现maven项目中多版本依赖兼容使用解决方案

    Maven依赖管理遵循“传递性”原则,即项目可以直接依赖其他项目,间接依赖也会被自动引入。当出现相同类路径的冲突时,Maven会遵循“第一声明者优先”原则,即先声明的依赖版本优先。 2. **排除依赖(Exclusions)...

    apache-maven3.5 依赖包

    2. **理解依赖传递性**:Maven会自动处理依赖的依赖,但可能会导致依赖冲突,需要通过 `<exclusions>` 标签排除不需要的子依赖。 3. **管理本地仓库**:定期清理无用的旧版本依赖,避免仓库过大影响性能。 4. **使用...

    webservices客户端maven依赖

    根据提供的文件信息,我们可以了解到这是一个基于Maven构建的Web服务客户端项目。该POM(Project Object Model)...通过对这些依赖的详细了解,可以帮助开发者更好地理解和维护项目,同时也有助于进一步扩展项目功能。

    maven依赖包

    此外,Maven的传递性依赖管理意味着,如果一个项目依赖A库,而A库又依赖B库,Maven会自动处理B库的下载,无需开发者手动介入。 在压缩包子文件的文件名称列表中提到的"org",这很可能是Maven依赖的组织...

    Maven依赖管理项目构建工具.pdf

    Maven依赖传递特性 Maven会自动处理项目的依赖及其依赖的依赖,称为依赖传递。 2. Maven依赖冲突特性 当不同依赖引入了相同但不同版本的库时,会产生依赖冲突。Maven遵循“nearest wins”原则解决冲突,但可能需要...

    Maven本地仓库依赖环境

    总的来说,理解并掌握Maven的本地仓库和依赖管理对于JavaWeb开发者至关重要,它能够简化项目构建过程,提高开发效率,并确保项目的一致性和可重复性。通过正确配置Maven和IDEA,你可以更轻松地管理和利用这些依赖,...

    maven 关于 scope test 和 继承传递的问题

    本文将深入探讨"Maven中scope test的使用以及依赖继承传递"这一主题,帮助开发者更好地理解和应用Maven的核心特性。 首先,`scope test`是Maven依赖管理中的一个关键概念。当我们在`pom.xml`文件中为某个依赖设置`...

    关于maven的资料关于maven的资料

    它会自动下载这些依赖及其传递性依赖到本地仓库,避免了手动管理jar文件的困扰。 3. **坐标**:在Maven中,每个项目都有唯一的坐标,由groupId、artifactId和version组成。例如,`com.example:my-app:1.0.0`,其中`...

    apache-maven-3.3.3.zip

    此外,Maven的依赖管理遵循“传递性依赖”原则,即如果项目A依赖于B,B又依赖于C,那么在构建项目A时,Maven会同时下载B和C。 Maven的生命周期是另一个关键特性,它定义了一系列构建阶段,如编译、测试、打包、部署...

    MavenHelper插件 帮你检查maven的各种冲突

    1. **依赖树查看**:MavenHelper提供了一个直观的依赖树视图,列出项目的所有依赖,包括直接依赖和传递依赖。你可以轻松查找特定的依赖并查看其版本信息。 2. **冲突检测**:该插件能自动检测出有版本冲突的依赖,...

    Maven权威指南_maven_

    Maven的依赖管理解决了版本冲突问题,通过设定依赖的传递性、范围(compile、runtime、test等)和排除机制,可以有效地管理项目间的依赖关系。 6. **聚合与继承** Maven的聚合项目可以将多个子项目组合在一起,...

    试试 IDEA 解决 Maven 依赖冲突的高能神器.docx

    总的来说,解决Maven依赖冲突需要理解其工作原理,结合IDEA提供的工具,如Maven Helper插件和依赖结构图,可以有效地定位和排除冲突,保持项目的稳定性和可维护性。在实际开发中,避免引入不必要的依赖和定期清理...

    maven历史版本下载

    3. **依赖管理**:3.5.2增强了依赖解析机制,能更准确地处理传递性依赖,避免了版本冲突。 4. **错误修复**:此版本修复了多个已知问题,提升了Maven的稳定性和可靠性。 要下载Maven的历史版本,可以访问Apache ...

Global site tag (gtag.js) - Google Analytics