Java构建工具:如何用Maven,Gradle和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,)
 
 
 - Module A
 
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.
- Maven: Introduction to the Dependency Mechanism is a good place to start:http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
 Gradle: a good starting point is Chapter 50 of its user guide:http://www.gradle.org/docs/current/userguide/dependency_management.html
Ant + Ivy: A slightly obfuscated path to the documentation can be found here:http://ant.apache.org/ivy/history/latest-milestone/ivyfile/dependency.html
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进行依赖管理的更多相关文章
- 高效使用Java构建工具,Maven篇|云效工程师指北
		
大家好,我是胡晓宇,目前在云效主要负责Flow流水线编排.任务调度与执行引擎相关的工作. 作为一个有多年Java开发测试工具链开发经验的CRUD专家,使用过所有主流的Java构建工具,对于如何高效使用 ...
 - 【构建工具】《Maven实战》读书笔记
		
Maven是我们在做Java开发过程中用经常用到的一个辅助工具.本篇博客是我学习Maven的一个记录博客,学习过程主要参考<Maven实战>这本书.同时也参考了Maven的官方文档. 1. ...
 - 浅谈java构建工具的选择
		
在学校的时候还总是自己用eclipse自带的jar导出工具,然后人工来给项目打包,那是相当的原始. 而后工作了,项目中都是用ant,慢慢的开始学会使用这个工具.感觉就和脚本一样,很容易读懂,做项目构建 ...
 - Lombok——一款Java构建工具,“懒人”必备!!(idea版)
		
一.简介 Lombok 是一种 Jav 构建工具,可用来帮助开发人员消除 Java 的冗长代码,尤其是对于简单的 Java 对象(POJO).它是通过注解实现这一目的. 二.使用 1.在idea中安装 ...
 - Java构建工具Ant小记(一)
		
Ant简介 Ant是基于java的构建工具.理论上来说它类似与make工具,但是却克服了make的一些固有的缺陷. 传统的Make是基于操作系统shell的构建工具,虽然也可以基于工作的os对make ...
 - java构建工具——ant使用
		
Ant是跨平台的构建工具,它可以实现项目的自动构建和部署等功能.在本文中,主要让读者熟悉怎样将Ant应用到Java项目中,让它简化构建和部署操作. 一.安装与部署 1.1 下载 下载地址:https: ...
 - SCons: 替代 make 和 makefile 及 javac 的极好用的c、c++、java 构建工具
		
http://scons.org/ https://www.ibm.com/developerworks/cn/linux/l-cn-scons/index.html 后附:另外,WAF是一个基于sc ...
 - Java构建工具_Ant详解
		
1,什么是antant是构建工具2,什么是构建概念到处可查到,形象来说,你要把代码从某个地方拿来,编译,再拷贝到某个地方去等等操作,当然不仅与此,但是主要用来干这个3,ant的好处跨平台 --因为 ...
 - 【系列教程1】Gradle入门系列三:依赖管理
		
在现实生活中,要创造一个没有任何外部依赖的应用程序并非不可能,但也是极具挑战的.这也是为什么依赖管理对于每个软件项目都是至关重要的一部分. 这篇教程主要讲述如何使用Gradle管理我们项目的依赖,我们 ...
 
随机推荐
- COM编程概述
			
所谓COM,英文为Componet Object Model,中文为组件对象模型(其实这种解释只有在考试卷上才具有一点实际意义). [1]为什么需要COM? COM是为了解决OLE问题而产生的.COM ...
 - stdout.read()与stdout.readlines()方法同时使用后果
			
stdout.read()与stdout.readlines()方法同时使用将无法导致最后使用的stdout.readlines()读取的内容为空,原因是首先调用的stdout.read()已将数据读 ...
 - page指令
			
<%@ page 属性1=“value” 属性2=“value2” ......%> page的属性有13种: 1)language --- 声明所使用的脚本语言的种类.(可省略) va ...
 - PHP序列化以及反序列化系列[1]--PHP序列化格式的写法
			
反序列化:对单一的已序列化的变量进行操作,将其转换回 PHP 的值(zval). PHP序列化方式 PHP在序列化的时候会将相应的变量以对应的键值进行储存. 将一个类序列化的话,处理代码主要的 文件: ...
 - Spring面试问题
			
什么是Spring框架?Spring框架有哪些主要模块? 使用Spring框架有什么好处? 什么是控制反转(IOC)?什么是依赖注入? 请解释下Spring中的IOC? BeanFactory和App ...
 - Xcode如何打包ipa安装包
			
http://jingyan.baidu.com/article/ceb9fb10f4dffb8cad2ba03e.html
 - 阿里云ECS主机多个网站配置,是有先后顺序的
			
注意:阿里云ECS主机多个网站配置,是有先后顺序的: 进入路径:/alidata/server/httpd-2.4.2/conf/vhosts/ 注意这个2.4.2是自己的版本不一样,但是找到 se ...
 - pipe row的用法, Oracle split 函数写法.
			
为了让 PL/SQL 函数返回数据的多个行,必须通过返回一个 REF CURSOR 或一个数据集合来完成.REF CURSOR 的这种情况局限于可以从查询中选择的数据,而整个集合在可以返回前,必须进行 ...
 - linux新内核的时钟机制代码
			
http://blog.chinaunix.net/uid-22810130-id-384173.html 如果说cfs是linux的一个很有创意的机制的话,那么linux中另一个创意就是nohz,我 ...
 - Windows上Python3.5安装Scrapy(lxml) 以及与twisted有关错误的解决
			
转载于:http://www.cnblogs.com/silverbullet11/p/4966608.html 常用网址: Python 3.5: https://www.python.org/do ...