您的位置:首页 > 其它

Maven学习笔记(一):Maven的坐标和依赖

2014-07-24 00:44 267 查看
在公司接触到了Maven,很快就被Maven不需要给项目手动拷贝无数jar包的特性给迷住了,果断火线突击学习Maven。由于刚接触这门技术,笔记若有错误,望海涵、指正。

Maven的坐标元素有5个:

-- groupId:用来指定项目名称,比如com.163.blog(示例而已,不要在意有没有这个东西),表示网易的博客项目

-- artifactId:用来指定项目下具体模块的名称,比如blog-security,表示博客项目下用户安全认证模块

-- version:用来指定由artifactId指定的模块的版本,比如0.0.1-SNAPSHOT等(版本有很多东西,楼主还没学到)

-- packaging:用来指定这个Maven项目的打包方式,即执行mvn package后输出的程序包的类型,默认为jar,也可以设置为war等

-- classfier:用来为这个Maven项目创建附属构建,比如blog-security-2.0.0-javadoc.jar、blog-security-2.0.0-src.jar等

这5个元素中,前三个(groupId,artifactId, version)元素是必须要被指定的,就如同寄快递时我们填写的邮寄地址,Maven只能通过分析这三个元素才能找到对应的依赖包。当执行mvn package命令后生成的程序包的名称通常为这个格式:artifactId-version[-classfier].packaging。

对于Maven的依赖,个人感觉还是比较简单,不过在实际开发中,可能最坑的就是排查依赖冲突了。

Maven通过在pom文件中使用<dependencies>标签来指定依赖集,使用子标签<dependency>来指定具体的依赖,指定依赖所使用的元素比较多:

-- groupId:作用同上

-- artifactId:作用同上

-- version:作用同上

-- type:指定依赖文件的类型,就是依赖的程序包的后缀名,如jar、war等

-- scope:指定依赖文件的依赖范围,即这个依赖文件可以作用于什么地方

-- compile:该范围包括编译、测试、运行 [比如 spring-core]

-- test:仅程序包在项目测试时进行依赖 [比如 jUnit]

-- runtime:程序包在项目测试和运行时进行依赖,在编译时不进行依赖,即项目在编译时无法使用这个程序包的功能。 【比如 JDBC驱动】

-- provided:程序包在编译和测试时进行依赖,运行时不进行依赖,[比如servlet-api] 程序包,由于web容器已提供,所以运行时不需要进行依赖。

-- system:这是一个高端的范围,没搞懂实际开发中能干嘛。

-- option:指定该依赖是否为可选依赖,可选依赖在依赖传递时不会被传递

-- exculsions:由于传递依赖的存在,可以使用子标签<exculsion>来排除传递依赖

传递性依赖的概念比较简单,就是项目A需要项目B的支持,项目B需要项目C的支持,那么A对于B就是第一直接依赖,B对于C就是第二直接依赖,此时C就是A的传递性依赖。简单来说,父亲依赖于爷爷,儿子依赖于父亲,如果要组成完整的三代同堂,那么儿子就是爷爷的传递性依赖。

如图:



传递性依赖本身也是一个依赖,只是它被传递了。所以传递性依赖同样拥有依赖范围。

-- 当第二直接依赖范围是compile时,传递性依赖的依赖范围和第一直接依赖相同

-- 当第二直接依赖范围是test时,传递性依赖不会被传递

-- 当第二直接依赖范围是provided时,只有当第一直接依赖为provided时才会被传递,依赖范围相同

-- 当第二直接依赖范围是runtime时,传递性依赖的依赖范围和第一直接依赖相同,除了当第一直接依赖为compile时,传递性依赖的范围是runtime。

由于我们不可能完全清楚我们显式依赖的程序包的所有传递性依赖,这就可能造成下面这个问题:

依赖1:A——>B——>C(1.0)

依赖2:D——>E——>C(2.0)

由于依赖1和依赖2都有传递性依赖C,并且C的版本不相同,这个是用Maven到底解析哪个版本呢?对于这种情况,Maven使用的解析策略为声明优先,即先声明的依赖的传递性依赖会被解析,而后声明的依赖的传递性依赖不会被解析。[声明优先]

另外一种情况,如下:

依赖1:A——>B——>M——>C(1.0)

依赖2:D——>E——>C(2.0)

由于依赖1传递性依赖C的路径长度为3,而依赖2的路径长度为2,所以Maven会解析使用依赖2的传递性依赖。[路径优先]

在声明依赖时使用<optional>元素来设置该依赖是否可选,指定为true则代表这个依赖可选,如下:

A——>B(1.0)

A——>B(2.0)

A依赖于B,但是B有两个版本,在配置时,即可将B的两个版本配置为可选依赖,在使用时再选择使用某个依赖。当然这种方法很不科学,所以基本不用配置可选依赖,而且可选依赖也不会被传递。面向对象思想强调单一职责,而不是大杂烩,所以可选依赖的配置完全是和OOP思想背道而驰的。

Maven依赖中的一个神器就是排除依赖,比如A依赖于B,B依赖于C的2.0版本,但是现在项目需要使用C的4.0版本,由于依赖的传递性,C的2.0版本会被Maven解析,所以这时候就可以i将C的2.0版本排除,然后另外显式声明C的4.0版本依赖。

排除依赖的声明示例

<dependencies>
<dependency>
<groupId>com.163.blog</groupId>
<artifactId>blog-security</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>com.qq.im</groupId>
<artifactId>project-a</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>


要排除的依赖只需要指定其groupId和artifactId即可,因为在依赖传递的过程中,这两个元素就可以完全描述一个依赖。即:在Maven解析后的依赖中,不可能出现groupId和artifactId相同,而version不同的多个依赖。

最后关于依赖的优化,由于依赖的传递性会导致很多依赖隐式的被加入项目中,这是可以执行mvn dependency:list来列出所有的依赖,使用mvn dependency:tree可以查看所有依赖的关系树结构,使用mvn dependency:analyze可以分析项目中哪些依赖是被使用了但是没有显式声明的,和那些依赖是显式声明了但是没有使用的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: