maven 依赖

来源:互联网 发布:json 转数组 编辑:程序博客网 时间:2024/04/30 22:05

有人认为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> 

groupId,必选,实际隶属项目,项目或者组织的唯一标志,并且配置时生成路径也是由此生成,如org.myproject.mojo生成的相对路径为:/org/myproject/mojo
artifactId,必选,其中的模块
version必选,版本号
type可选,依赖类型,默认jar
scope可选,依赖范围,默认compile
optional可选,标记依赖是否可选,默认false
exclusion可选,排除传递依赖性,默认空
packaging:打包机制,如pom,jar,maven-plugin,ejb,war,ear,rar,par
name:用户描述项目的名称,无关紧要的东西,可选
url:应该是只是写明开发团队的网站,无关紧要,可选
classifer:分类

其中groupId,artifactId,version,packaging这四项组成了项目的唯一坐标。一般情况下,前面三项就可以组成项目的唯一坐标了。

上例中我们声明了一个对junit的依赖,它的groupId是junit, artifactId是junit, version是4.4。这一组GAV构成了一个Maven坐标,基于此,Maven就能在本地或者远程仓库中找到对应的junit-4.4.jar文件。

分类依赖
当同一个模块,所依赖的几个模块版本都相同时,可以使用maven里的属性做分类依赖,依赖版本升级时改一处即可。
为了管理其版本,你对它们进行过统一的升级,逐个的将version改成了最新版。但是,显然,当POM很大的时候你说不定会犯错误,而当版本不一致的时候,一些诡异的兼容性问题就可能出现。
对此,Maven有它的解决方案:

<properties>  //定义一个变量,提高复用性  <spring.version>2.5</spring.version>  </properties>  <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>  

这里我们定义了一个Maven属性,其名称为spring.version,值是2.5。在这个POM中,我们就能用spring.versionspringversion{spring.version},当Maven运行的时候,它会自动用值2.5来替换这个引用。
当我们需要升级spring的时候,只要更改一个地方便可,而且,你现在能很高的保证所有的spring依赖包都是同一个版本。

依赖范围(scope)
maven项目有三种classpath(编译,测试,运行)
scope用来表示与classpath的关系,总共有五种:
1)compile:编译,测试,运行
2)test:测试
3)provided:编译,测试
4)runtime:运行
5)system:编译,测试,同provided,但必须指定systemPath,慎用

本文的第一个例子其实是有漏洞的,对于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。

传递性依赖
顾名思义,你懂的,但是传递的范围会发生改变,这个由maven自身处理,只要理解下即可
第一列为第一依赖,第二列为第二依赖,单元格为传递范围
这里写图片描述

依赖调解
传递路径长度取最短原则,传递路径长度相等时,采取最先申明原则
可选依赖
尽量少用,可选依赖不会被传递,需要显式申明
排除依赖
发现依赖包里有些包不稳定,可以排除依赖,显式的申明文档的包

<dependency>        <groupId>javax.mail</groupId>      <artifactId>mail</artifactId>      <version>1.4.1</version>      <exclusions>          <exclusion>              <groupId>javax.activation</groupId>              <artifactId>activation</artifactId>          </exclusion>      </exclusions>  </dependency>  <dependency>      <groupId>javax.activation</groupId>      <artifactId>activation</artifactId>      <version>1.1</version>  </dependency>  

优化依赖
可概括为三个命令
1)mvn dependency:list
表示依赖列表,maven eclipse插件已经实现,有图形化显示,在pom.xml的dependencies页
这里写图片描述

2)mvn dependency:tree
表示依赖列表,maven eclipse插件已经实现,有图形化显示,在pom.xml的dependency hierarchy页
这里写图片描述

3)mvn dependency:analyze
查找出在编译和测试中未使用但显示声明的依赖
这里写图片描述

分类器(classifer)
GAV是Maven坐标最基本最重要的组成部分,但GAV不是全部。还有一个元素叫做分类器(classifier),90%的情况你不会用到它,但有些时候,分类器非常不可或缺。
举个简单的例子,当我们需要依赖TestNG的时候,简单的声明GAV会出错,因为TestNG强制需要你提供分类器,以区别jdk14和jdk15,我们需要这样声明对TestNG的依赖:
Xml代码 收藏代码

<dependency>    <groupId>org.testng</groupId>    <artifactId>testng</artifactId>    <version>5.7</version>    <classifier>jdk15</classifier>  </dependency>  

你会注意到maven下载了一个名为testng-5.7-jdk15.jar的文件。其命名模式实际上是--.。理解了这个模式以后,你就会发现很多文件其实都是默认构件的分类器扩展,如 myapp-1.0-test.jar, myapp-1.0-sources.jar。
分类器还有一个非常有用的用途是:我们可以用它来声明对test构件的依赖,比如,我们在一个核心模块的src/test/java中声明了一些基础类,然后我们发现这些测试基础类对于很多其它模块的测试类都有用。没有分类器,我们是没有办法去依赖src/test/java中的内容的,因为这些内容不会被打包到主构件中,它们单独的被打包成一个模式为--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权威指南》 下载。

0 0
原创粉丝点击