maven 实践 :管理依赖
有人认为Maven是一个依赖管理工具,当然这种想法是错误的(确切的说Maven是一个项目管理工具,贯穿了整个项目生命周期,编译,测试,打包,发布...),但Maven给人造成这种错误的印象也是有原因的,因为Maven的依赖管理十分强大,用好了Maven,你不再需要面对一大堆jar感到头大,依赖冲突,无用依赖等问题也能够得到有效的防止和解决。本节介绍如何用好Maven的依赖管理。
最简单的依赖
依赖是使用Maven坐标来定位的,而Maven坐标主要由GAV(groupId, artifactId, version)构成。因此,使用任何一个依赖之间,你都需要知道它的Maven坐标,关于如何寻找Maven坐标,《搜索Maven仓库》 一文可以帮助你。
最简单的依赖如:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
</dependency>
上例中我们声明了一个对junit的依赖,它的groupId是junit, artifactId是junit, version是4.4。这一组GAV构成了一个Maven坐标,基于此,Maven就能在本地或者远程仓库中找到对应的junit-4.4.jar文件。
依赖归类
随着项目的增大,你的依赖越来越多,比如说你依赖了一堆spring的 jar,有org.spring.framework:spring-core, org.spring.framework:beans, org.spring.framework:spring-web, org.spring.framework:spring-mock。它们的groupId是相同的,artifactId不同。为了管理其版本,你对它 们进行过统一的升级,逐个的将version改成了最新版。但是,显然,当POM很大的时候你说不定会犯错误,而当版本不一致的时候,一些诡异的兼容性问 题就可能出现。
对此,Maven有它的解决方案:
<dependencies>
<dependency>
<groupId>org.spring.framework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.spring.framework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.spring.framework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.spring.framework</groupId>
<artifactId>spring-mock</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies> <properties>
<spring.version>2.5</spring.version>
</properties>
这里我们定义了一个Maven属性,其名称为spring.version,值是2.5。在这个POM中,我们就能 用${spring.version}的方式来引用该属性。我们看到,所有spring相关的依赖的version元素现在都成 了${spring.version},当Maven运行的时候,它会自动用值2.5来替换这个引用。
当我们需要升级spring的时候,只要更改一个地方便可,而且,你现在能很高的保证所有的spring依赖包都是同一个版本。
依赖范围(scope)
本文的第一个例子其实是有漏洞的,对于Junit,一般来说你只有在运行测试的时候需要它,也就是说,它对于src/main/Java的classpath没什么意义,并且,将Junit的jar文件打入最终的发布包也不是好事,这无谓的增加了发布包的大小。
其实我们应该这样做:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</test>
</dependency>
于是,junit对于主源码classpath不可用,对于测试源码classpath可用,不会被打包。
再举个例子,在开发javaee应用的时候我们一定会用到servlet-api,它对于主源码和测试源码都是必要的,因为我们的代码中会引入 servlet-api的包。但是,在打包的时候,将其放入WAR包就会有问题,因为web容器会提供servlet-api,如果我们再将其打包就会造 成依赖冲突,
解决方案如下:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
将依赖范围设置成provided,就意味着该依赖对于主源码classpath,以及测试classpath可用,但不会被打包。这正是servlet-api所需要的。
这里归纳一下主要的依赖范围以及作用:
| 依赖范围(scope) | 主源码classpath可用 | 测试源码classpath可用 | 会被打包 |
| compile 缺省值 | TRUE | TRUE | TRUE |
| test | FALSE | TRUE | FALSE |
| runtime | FALSE | TRUE | TRUE |
| provided | TRUE | TRUE | FALSE |
需要注意的是,当我们没有声明依赖范围的时候,其默认的依赖范围是compile。
分类器(classifer)
GAV是Maven坐标最基本最重要的组成部分,但GAV不是全部。还有一个元素叫做分类器(classifier),90%的情况你不会用到它,但有些时候,分类器非常不可或缺。
举个简单的例子,当我们需要依赖TestNG的时候,简单的声明GAV会出错,因为TestNG强制需要你提供分类器,以区别jdk14和jdk15,
我们需要这样声明对TestNG的依赖:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.7</version>
<classifier>jdk15</classifier>
</dependency>
你会注意到maven下载了一个名为testng-5.7-jdk15.jar的文件。其命名模式实际上 是<artifactId>-<version>-<classifier>.<packaging>。 理解了这个模式以后,你就会发现很多文件其实都是默认构件的分类器扩展,如 myapp-1.0-test.jar, myapp-1.0-sources.jar。
分类器还有一个非常有用的用途是:我们可以用它来声明对test构件的依赖,比如,我们在一个核心模块的src/test/java中声明了一些基 础类,然后我们发现这些测试基础类对于很多其它模块的测试类都有用。没有分类器,我们是没有办法去依赖src/test/java中的内容的,因为这些内 容不会被打包到主构件中,它们单独的被打包成一个模式为<artifactId>-<version>-test.jar的文 件。
我们可以使用分类器来依赖这样的test构件:
<dependency>
<groupId>org.myorg.myapp</groupId>
<artifactId>core</artifactId>
<version>${project.version}</version>
<classifier>test</classifier>
</dependency>
理解了分类器,那么可供依赖的资源就变得更加丰富。
依赖管理(dependencyManagement)
当你只有一个Maven模块的时候,你完全不需要看这个部分。但你心里应该清楚,只有一个Maven模块的项目基本上只是个玩具。
实际的项目中,你会有一大把的Maven模块,而且你往往发现这些模块有很多依赖是完全项目的,A模块有个对spring的依赖,B模块也有,它们 的依赖配置一模一样,同样的groupId, artifactId, version,或者还有exclusions, classifer。细心的分会发现这是一种重复,重复就意味着潜在的问题,Maven提供的dependencyManagement就是用来消除这种 重复的。
正确的做法是:
1. 在父模块中使用dependencyManagement配置依赖
2. 在子模块中使用dependencies添加依赖
dependencyManagement实际上不会真正引入任何依赖,dependencies才会。但是,当父模块中配置了某个依赖之后,子模块只需使用简单groupId和artifactId就能自动继承相应的父模块依赖配置。
这里是一个来自于《Maven权威指南》的例子:
父模块中如此声明:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0.0</version>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.2</version>
</dependency>
...
<dependencies>
</dependencyManagement>
子模块中如此声明:
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>project-a</artifactId>
...
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
你依赖配置越复杂,依赖管理所起到的作用就越大,它不仅能够帮助你简化配置,它还能够帮你巩固依赖配置,也就是说,在整个项目中,对于某个构件(如MySQL)的依赖配置只有一种,这样就能避免引入不同版本的依赖,避免依赖冲突。
小结
本文讲述了一些Maven依赖中重要的概念,并通过样例提供了一些最佳实践,如依赖归类,依赖范围,分类器,以及依赖管理。我的目的是通过浅显的例子讲述那些你实际工作中会需要了解的80%的内容,如果你需要更深入的了解,请参考《Maven权威指南》 。
maven 实践 :管理依赖的更多相关文章
- POM(project Object Model) Maven包管理依赖 pom.xml文件
什么是POM POM全称为“Project Object Model”,意思是工程对象模型.Maven工程使用pom.xml来指定工程配置信息,和其他文本信息.该配置文件以xml为格式,使用xml语法 ...
- maven 统一管理依赖的版本号
- Maven最佳实践:管理依赖
From:http://juvenshun.iteye.com/blog/337405 Maven最佳实践:管理依赖 "If I have seen further it is by sta ...
- maven冲突管理及依赖管理实践
1.“最近获胜策略(nearest wins strategy)”的方式处理依赖冲突 Maven采用“最近获胜策略(nearest wins strategy)”的方式处理依赖冲突,即如果一个项目最终 ...
- 什么是Maven? 使用Apache Maven构建和依赖项管理
通过优锐课java架构学习中,学到了不少干货,整理分享给大家学习. 开始使用最流行的Java构建和依赖管理工具Maven Apache Maven是Java开发的基石,也是Java使用最广泛的构建管理 ...
- WebMagic基础与Maven管理依赖
2. 快速开始 WebMagic主要包含两个jar包:webmagic-core-{version}.jar和webmagic-extension-{version}.jar.在项目中添加这两个包的依 ...
- Maven教程3(依赖管理)
Maven教程2(Eclipse配置及maven项目) Maven项目,依赖,构建配置,以及构件:所有这些都是要建模和表述的对象.这些对 象通过一个名为项目对象模型(Project Object Mo ...
- Maven实战07_依赖
1:依赖声明 <project> ... <dependencies> <dependency> <groupId>...</groupId> ...
- 使用maven来管理您的java项目
maven是一个项目管理工具,使用maven可以自动管理java项目的整个生命周期,包括编译.构建.测试.发布和报告等.在大型项目开发中,使用maven来管理是必不可少的. 一.安装maven 1.W ...
随机推荐
- docker使用(一)
docker相对于虚拟技术: 更高效的利用系统资源 更快的启动速度 一致的运行环境 持续交付和部署 更加轻松的迁移 更加轻松的维护和扩展 什么是docker镜像,容器: 可以说他就是一个模型,用面向对 ...
- docker环境下构建flannel 网络
flannel 是coreos 开发的网络解决方案,为每一台主机分配一个 subnet,容器从此subnet 中分配ip,ip可以在主机间路由.每个subnet从更大的ip池中划分,为了在各个主机间共 ...
- Why mobile web apps are slow
http://sealedabstract.com/rants/why-mobile-web-apps-are-slow/ I’ve had an unusual number of interest ...
- 20155327 实验一《Java开发环境的熟悉》实验报告
实验内容 1.使用JDK编译.运行简单的Java程序: 2.使用Eclipse 编辑.编译.运行.调试Java程序. 实验知识点 JVM.JRE.JDK的安装位置与区别: 命令行运行javac:jav ...
- c++ 时间函数和结构化数据
time和localtime 数据结构概念 struct关键字 认识数据结构 自定义结构 例:获取当前系统日期和时间;(代码例子) 一.函数: time 函数time()返回的是当 ...
- RHSCA模拟考试
开始考试:桌面是个黑框子 点击reboot按钮,破解密码 开机成功,输入startx进入图形界面 不能复制,要在物理机用ssh root@172.25.0.11 远程连接,就可以复制粘贴了 * Hos ...
- wpf TabControl控件的SelectionChanged方法
对于老手来说很简单,但是新手我从百度上找了好久没找到,最后还是去谷歌找到的,哎,万能的google. 前端界面: <TabControl Margin="0,10,0,0" ...
- Redis实现之客户端
客户端 Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复.通过使用 ...
- pyhon3.0 day01 变量、输入、输出、循环
pyhon3.0 基础01 1 python解释器 Python的解释器很多,但使用最广泛的还是CPython.如果要和Java或.Net平台交互,最好的办法不是用Jython或IronPython, ...
- Jmeter如何做接口测试
最近在学习Jmeter,记录下来,与大家分享. 1. 打开Jmeter,我用的是5.0版本,打开后如下图所示: 2. 右键测试计划,添加 - 线程(用户) - 线程组,如下如图所示: 3. 右键线程组 ...