原文来自:https://zeroturnaround.com/rebellabs/java-build-tools-how-dependency-management-works-with-maven-gradle-and-ant-ivy/

编译的时候可以运行,运行时出问题

在当今java项目自动化构建的场景当中,依赖管理已经成为了项目构建自动化工具中的一个主要部分,但是在过去并总是这样。

回想以前那个很爽的时候,你的项目依赖性管理只需要将jar包导入到lib文件夹中,然后将其加入到你的版本控制系统中。

或者可以采取更冒险的方式,你可以写一个从外部源中下载想要的版本的库文件的脚本,还要向神仙保佑外部的URL不会改变。

用手动的方式来完成所有的这些将成为一个令人怯步而且麻烦的过程,尤其是考虑到透明的依赖(依赖的依赖),也许只有在某个库文件的代码执行的时候才会出现。

这会造成在运行过程中发生某些意向不到的麻烦,即使是在编译过程能够修复的问题也会发现和解析出有透明依赖。

幸运的是,在如今的项目构建自动化工具中这种情形不再是主要问题(这句话翻得有问题)。

项目的模块化变得越来越受欢迎,内部项目和外部依赖的需求也增加了,结果所有常用的项目自动化构建工具针对这种挑战进行加强了并且支持依赖性管理;不论是out of box还是通过插件。

为了更加方便开发者,它们都是用相同的语法来定义依赖性,同时也可以从相同的公共的资料库(public artifact repositories)中抽取依赖性关系(比如Maven Central)。

但是和这些公共的资料库一样方便的同时,它们也为项目带来了复杂性;透明依赖的冲突,而且增加了对于远程库的依赖。

如何定义依赖性

用于定义依赖性最常用的语法是加入group-id,artifact-id和需要的版本号到构建脚本的依赖性部分。项目构建工具会试图解析这些依赖性,通过在它们的局部环境中和远程定义的库中。

在下面的例子中,将会向你展示如何Google的公共资源库Guava的依赖性。在开始之前,你得知道它的group-id(com.google.guava),它的artifact-id(guava),和你感兴趣的库的版本号,

(在这里以最近的版本“15.0”为例)。手上有了这些信息之后,你可以往构建脚本的依赖性部分加入这些依赖。

以下是在Maven,Gradle和Ant Ivy中的示例:

Maven

Gradle

Ant+Ivy(ivy.xml,或者在build.xml中)

现在你可以知道之前的参数是如何在这三个例子中使用的,在Maven中可能稍显累赘一点。

在构建工具中,property的名称可能会有些许不同,但大体上还是类似的。

如何使用版本范围

What if you don’t want to depend on a specific version of a library, but know that any version in a specific range will do? Or if you know that any version except ‘X’ will work? This is where version ranges can come in handy.

Instead of just a single version number, you can also specify a range, using a notation very similar to how you would define an interval in mathematics: parenthesis ( ) work as exclusive markers and brackets [ ] as inclusive markers. Using a range allows the tool to pick a suitable version for the build. Continuing the example with Guava, if you know that any version between 12.0 and up to 15.0 (excluded) would work, you could define it as “[12.0,15.0)”. You can also leave it open-ended like “[12.0,)”, which adds a requirement for any version from 12.0 and up, similarly “(,12.0]” would be any version up to and including 12.0. Or if you want anything higher than 12.0, except for version 13.0, you could specify “[12.0,13.0),(13.0,)” as the version.

But why use ranges at all? It’s an easy and convenient way to get a newer version of the library without having to change your build script; however, it also sets you up for potential trouble, should the author of the library opt to change functionality or the API that you’re relying on! Another caveat about using ranges is that if the version numbering is inconsistent or doesn’t follow some standard, things might not go as expected. Using ranges on artifacts with qualifiers in the version string (like SNAPSHOT, ALPHA, BETA etc) also doesn’t always go as expected, as the range definition only supports numerical intervals and the build tool might pick a beta version because it has a higher number than the release version.

Besides ranges and specific versions, dependencies can also be resolved using dynamic versions by using the keywords ‘LATEST’ or ‘RELEASE’ instead of the version number. Using those will make the build tool inquire the artifact repository about which version is the latest (or latest release) and use that version. The same caveats apply here as with version ranges though–any changes to the API or functionality might break the world.

Transitive dependencies and dependency conflicts

Let’s go back in time to the “good old days” again. Here, you had full control and an overview over which libraries were used, since it was immediately visible which libraries were present in the lib-folder. But with declarative dependencies remotely available, and the transitive dependencies that are automatically included as well, this easy overview of which libraries are in use have become somewhat obscured. Luckily, most build tools have a plugin or an option to list the entire dependency tree:

  • Maven: mvn dependency:tree -Dverbose
  • Gradle: gradle -q dependencies
  • Ivy: <report conf="compile" />

Ivy’s report option allows you to generate the reports in various different formats, the default being an HTML and graphml report; so not as straight-forward as the console output you get from Maven and Gradle.

But what happens if there are conflicts in the dependencies? What if Library A and Library B both depend on Library C, but require different versions of it? This is where things start to get a bit tricky, and where build tools’ dependency management implementations diverge.

Assuming we have a project dependency structure that looks something like this:

  • Project
    • Module A
      • com.google.guava:guava:[11.0,12.99]
    • Module B
      • com.google.guava:guava:[13.0,)

Trying to build the above project with Maven will result in an error because the dependencies could not be resolved, since no version of Guava can satisfy both ranges. But if you use an equivalent Gradle build script and build it with Gradle, it will pick the highest version of Guava available in either of the ranges; which in this case means version ’15.0’. Changing the dependency of Module B, so its range is ‘[12.0,)’, Maven will now pick version ‘12.0.1’, which satisfies both ranges; Gradle still picks version ’15.0’.

Ivy and Gradle acts very similar in these scenarios, which isn’t that surprising considering Gradle originally used Ivy as their underlying dependency management implementation until they implemented their own dependency resolution engine.

The usage of ranges isn’t that widely-used though, and the more common use case is to just have the simple version number listed in the dependency. Even in this simple case, Maven, Gradle and Ivy again act vastly different when resolving dependencies!

Maven utilizes a “nearest definition” strategy, in which the closest version to the root is the version it’ll use throughout the entire build! In the case of the structure above, Module A and Module B both depend on Guava, and they are both found at the same depth; but since Module A is listed in the project before Module B, the dependency for Guava used there will be the version used for the entire build, even if the latter relies on a higher version!

Due to this, a common approach for having better control over which version of conflicting dependencies is used is to add a dependency to the wanted version in the parent pom file. Since this is the first pom to be parsed, the dependencies listed there will always be nearest, thus Maven should use that version and ignore every other version mentioned in the dependency graph.

As opposed to Maven, both Gradle and Ivy by default resolve dependency conflicts with a simple strategy: they just select the highest version :) If this strategy doesn’t suit your needs, you can select a different strategy: for instance, force the build to fail should a dependency conflict arise, or forcing a specific version of the dependency to be used, overriding any version otherwise defined as part of the dependency graph.

Final words

While the above was just a short introduction to some of the great and not so great things about dependency management, these are some of the things to keep in mind when dealing with it, and a good excuse to start reading up on the documentation for your build tool to see exactly what’s happening behind the screen.

Even though keeping binary dependencies in your VCS is somewhat frowned upon today, the idea of having complete control over which libraries you include is still a good idea. No matter which build tools you prefer (or are required to use), it’s always a good idea to know how they handle your dependencies; an old version of a library might introduce bugs, strangeness or, in the worst case, security risks to your production system! Keeping a handle on this can potentially save you a lot of headaches down the road.

Java构建工具:如何用Maven,Gradle和Ant+Ivy进行依赖管理的更多相关文章

  1. 高效使用Java构建工具,Maven篇|云效工程师指北

    大家好,我是胡晓宇,目前在云效主要负责Flow流水线编排.任务调度与执行引擎相关的工作. 作为一个有多年Java开发测试工具链开发经验的CRUD专家,使用过所有主流的Java构建工具,对于如何高效使用 ...

  2. 【构建工具】《Maven实战》读书笔记

    Maven是我们在做Java开发过程中用经常用到的一个辅助工具.本篇博客是我学习Maven的一个记录博客,学习过程主要参考<Maven实战>这本书.同时也参考了Maven的官方文档. 1. ...

  3. 浅谈java构建工具的选择

    在学校的时候还总是自己用eclipse自带的jar导出工具,然后人工来给项目打包,那是相当的原始. 而后工作了,项目中都是用ant,慢慢的开始学会使用这个工具.感觉就和脚本一样,很容易读懂,做项目构建 ...

  4. Lombok——一款Java构建工具,“懒人”必备!!(idea版)

    一.简介 Lombok 是一种 Jav 构建工具,可用来帮助开发人员消除 Java 的冗长代码,尤其是对于简单的 Java 对象(POJO).它是通过注解实现这一目的. 二.使用 1.在idea中安装 ...

  5. Java构建工具Ant小记(一)

    Ant简介 Ant是基于java的构建工具.理论上来说它类似与make工具,但是却克服了make的一些固有的缺陷. 传统的Make是基于操作系统shell的构建工具,虽然也可以基于工作的os对make ...

  6. java构建工具——ant使用

    Ant是跨平台的构建工具,它可以实现项目的自动构建和部署等功能.在本文中,主要让读者熟悉怎样将Ant应用到Java项目中,让它简化构建和部署操作. 一.安装与部署 1.1 下载 下载地址:https: ...

  7. SCons: 替代 make 和 makefile 及 javac 的极好用的c、c++、java 构建工具

    http://scons.org/ https://www.ibm.com/developerworks/cn/linux/l-cn-scons/index.html 后附:另外,WAF是一个基于sc ...

  8. Java构建工具_Ant详解

    1,什么是antant是构建工具2,什么是构建概念到处可查到,形象来说,你要把代码从某个地方拿来,编译,再拷贝到某个地方去等等操作,当然不仅与此,但是主要用来干这个3,ant的好处跨平台   --因为 ...

  9. 【系列教程1】Gradle入门系列三:依赖管理

    在现实生活中,要创造一个没有任何外部依赖的应用程序并非不可能,但也是极具挑战的.这也是为什么依赖管理对于每个软件项目都是至关重要的一部分. 这篇教程主要讲述如何使用Gradle管理我们项目的依赖,我们 ...

随机推荐

  1. Java :List

    1.List是一个接口,不能实例化,需要实例化一个ArrayList或者LinkedListList myList = new ArrayList(); 2.List中可以添加任何对象,包括自己定义的 ...

  2. 在MVC里面使用Response.Redirect方法后记得返回EmptyResult

    在ASP.NET MVC中我们很多时候都会在拦截器和Controller中直接使用Response.Redirect方法做跳转,但是实际上Response.Redirect方法执行后ASP.NET并不 ...

  3. SVN使用(二)

    TortoiseSVN是windows平台下Subversion的免费开源客户端. 一般我们都是先讲讲服务器的配置,然后再讲客户端的使用,但是在TortoiseSVN上,却可以反过来.因为,如果你的要 ...

  4. 试用版SQL Server 2008 R2 提示评估期已过

    解决SQL Server 2008提示评估期已过第一步:进入SQL2008配置工具中的安装中心第二步:再进入维护界面,选择版本升级第三步:进入产品密钥,输入密钥第四步:一直点下一步,直到升级完毕.SQ ...

  5. linux 文件删除原理

    文件删除: i_link 文件的硬连接数 i_count 引用计数(有一个程序使用i_count加1) 文件删除的条件: i_link=0 and i_count=0 被进程占用的文件可以删除

  6. filesort是什么意思?

    我们碰到有order by 或者group by,或者distinct语句的时候,如果查看执行计划,通常会看到using filesort的字眼,那么这个filesort是不是真是文件排序呢?其实不然 ...

  7. Backup: Array in Perl6

    Array in Perl6 继承List,而List又继承Iterable,Positional,Cool ARRAY.pop ARRAY.shift ARRAY.push: VALUES ARRA ...

  8. 在keil 4中添加stc系列芯片的方法--【sky原创】

    在keil 4中添加stc系列芯片的方法: 1.从官网下载uv3.cdb的文件网址是:http://www.stcmcu.com/ 2.下载好后把uv3.cdb文件改成STC.cdb:3. 然后将[S ...

  9. 【jQuery UI 1.8 The User Interface Library for jQuery】.学习笔记.2.更换主题

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  10. python中时间的基本使用

    格式化日期 我们可以使用 time 模块的 strftime 方法来格式化日期,: time.strftime(format[, t]) #!/usr/bin/python # -*- coding: ...