《Maven实战》整理三:坐标和依赖

1.1 何为Maven坐标

正如之前所说的,Maven的一大功能就是管理项目依赖。为了能自动化地解析任何一个Java构件,Maven就必须将它们唯一标识,这就依赖管理的底层基础——坐标。

1.2 坐标详解

Maven坐标的元素包括:groupId,artifactId、version、packaging、classifier。先看一组坐标定义,如下:

<groupId>org.sonatype.nexus</groupId>
<artifactId>nexus-indexer</artifactId>
<version>2.0.0</version>
<packaging>jar</packaging>

这是nexus-indexer的坐标定义,nexus-indexer是一个对Maven仓库编纂索引并提供搜索功能的类库,它是Nexus项目的一个子模块。下面详细解释一下各个坐标元素:

  • groupId:定义当前Maven项目隶属的实际项目。上例中,groupId为org.sonatype.nexus,org.sonatype表示Sonatype公司创建的一个非营利性组织,nexus表示Nexus这一实际的项目,该groupId与域名nexus.sonatype.org对应,即该groupId表示Sonatype公司的Nexus项目。
  • artifactId:该元素定义实际项目中的一个Maven项目(模块),推荐的做法是使用项目名称作为artifactId的前缀。比如上例中的artifactId是nexus-indexer,使用了实际项目名nexus作为前缀,这样可以方便寻找实际构件。之后使用了indexer表示其所属的具体子项目(模块)。
  • version:该元素定义了Maven项目所处的版本,如上例中的nexus-indexer的版本是2.0.0。
  • packaging:该元素定义Maven项目的打包方式,默认值是jar。打包方式与所生成构件的文件拓展名对应,如上例中的packaging为jar,最终的文件名为nexus-indexer-2.0.0.jar。
  • classifier:该元素用来帮助定义构建输出一些附属构件,让附属构建也拥有唯一的坐标。

上述5个元素中,groupId、artifactId、version是必须定义的,packagnig是可选的(默认为jar),而classifier是不能直接定义的。

同时,项目构建的文件名是与坐标相对应的,一般的规则为artifactId-version[-classifier].packaging,其中[-classifier]表示可选。比如上例的nexus-indexer的主构件为nexus-indexer-2.0.0.jar,附属构建有nexus-indexer-2.0.0-javadoc.jar。

1.3 依赖

通过上面的学习,我们配置了一个Maven项目的groupId, artifactId等信息。当我们需要进行开发的时候,我们就需要导入我们这个项目所依赖的一些构建(Jar包)。假设这时候我们需要导入一个用于JUnit测试的依赖,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <groupId>org.sonatype.nexus</groupId>
    <artifactId>nexus-indexer</artifactId>
    <version>2.0.0</version>
    <packaging>jar</packaging>

    <dependencies>
        <!--dependency>
          <groupId>com.chanshuyi.demo</groupId>
          <artifactId>[the artifact id of the block to be mounted]</artifactId>
          <version>1.0-SNAPSHOT</version>
        </dependency-->

        <!-- JUnit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

通过上面我们可以看到,导入依赖是在<dependencies>元素下的<dependency>元素进行配置。通过查询我们可以知道JUnit这个依赖的groupId,artifactId都是:junit,并且我们需要的是4.10版本,依赖的范围为test(即测试范围内可用)。其实一个标准的依赖可以包含的元素有:

<project>
    ...
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...</optional>
            <exclusions>
                <exclusion>
                ..
                </exclusion>
            </exclusions>
        <dependency>
        ...
    ...
</project>

其中:

  • groupId、artifactId和version:这是依赖的基本坐标,Maven通过坐标找到依赖的构件。你可以通过这里查询构建的这3个元素信息。
  • type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素必须声明,默认值为jar。
  • scope:依赖的范围,下文详解。
  • optional:标记该依赖是否可选,下文详解。
  • exclusions:用来排除传递性依赖,下文详解。

1.4 依赖范围

上面提到,JUnit依赖的测试范围是test,测试范围用元素scope表示。首先需要知道,Maven在编译项目主代码的时候需要使用一套classpath,Maven在编译和执行测试的时候会使用另外一套classpath,实际运行的时候又需要一套classpath。如果在引入依赖的时候指定了scope,那么该依赖只在指定范围内有效。比如上面引入的JUnit的scope为test,则该依赖只在执行测试的时候有效,在运行的时候该依赖是无效的。因此当你尝试在主代码中引入JUnit时,你会发现Junit依赖并不存在。

依赖范围就是用来控制依赖与这三种classpath(编译classpath、测试classpath、运行classpath)的关系,Maven有以下几种依赖范围:

  • compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。典型的例子是spring-core依赖。
  • test:测试依赖范围。只对测试classpath有效,典型的例子是Junit依赖。
  • provided:已提供依赖范围。对于编译和测试classpath有效,典型的例子是servlet-api依赖。
  • runtime:运行时依赖范围。对于测试和运行有效,典型的例子是JDBC驱动依赖。
  • system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是使用system依赖范围的依赖必须通过systemPath元素显示地制定依赖文件的路径。由于此类依赖往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。如:
<dependency>
    <groupId>javax.sql</groupId>
    <artifactId>jdbc-stdext</artifactId>
    <version>2.0</version>
    <scope>system</scope>
    <systemPath>${java.home)/lib/rt.jar</systemPath>
<dependency>
  • import(Maven2.0.9及以上):导入依赖范围。了解即可,将在后面详细说到。

  [可以补充一个依赖与classpath关系的表格 MARK]

1.5 传递性依赖

1.5.1 何为传递性依赖

我有一个Maven项目account-mail,它有一个compile范围的spring-core依赖,spring-core有一个compile范围的commons-logging依赖,那么commons-logging就会成为account-email的compile范围依赖,commons-logging就是account-email的一个传递性依赖,如图:

1.5.2 传递性依赖和依赖范围

如果A->B->C,即A对于B的依赖范围是compile,B对于C的依赖范围是compile,那么A对于C的依赖范围是compile。此时,我们称A对于B是第一直接依赖,B对于C是第二直接依赖(根据情况的不同可能会有第三、第四……直接依赖),A对于C是传递性依赖。

那么当A对于B是test范围的依赖,B对于C是compile范围的依赖,那么A对于C的依赖是什么范围呢?下面我们通过一个表格来详细讲解:

最左边的一列表示第一直接依赖的范围,最上面的一行表示第二直接依赖的范围,中间的交叉单元格则表示传递性依赖范围的结果。

如果A->B->C的传递性依赖中,如果A->B是compile依赖,B->C是runtime依赖,那么A->C就是runtime范围的依赖。

1.6 依赖调解

1.6.1 第一原则:路径最近者优先

例如在项目中可能有这样的依赖关系:A->B->C->X(1.0),A->D->X(2.0),这个时候X是A的传递性依赖,那我们该取那个为A的依赖呢?根据Maven依赖调解第一原则,我们知道应该取X(2.0)为A的依赖。

1.6.2 第二原则:第一声明者优先

当依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用。比如这样的依赖关系:A->B->Y(1.0)、A->C->Y(2.0),这时候Y(1.0)和Y(2.0)的路径长度是一样的,都为2。如果B的依赖声明在C之前,那么Y(1.0)就会被解析使用。

1.7 可选依赖

假设有这样一个依赖关系,项目A依赖与项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖:A->B、B->X(可选)、B->Y(可选),那么X、Y就是A的传递性依赖。然而,由于这里X、Y是可选依赖,依赖将不会得以传递。比如B是一个持久层隔离工具包,它支持多种数据库,包括MySQL、PostgreSQL等,在构建这个工具包的时候,需要这两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库。这时候MySQL、PostgreSQL这两个依赖对于B就是可选的。此时项目B的依赖声明代码清单如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>1.0.0</modelVersion>
    <groupId>com.juvenxu.mvnbook</groupId>
    <artifactId>project-b</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>1.1.10</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>8.4-701.jdbc3</version>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

在理想情况下,是不应该使用可选依赖的。因为在面向对象设计中,有单一职责原则,意指一个类只有一项职责。应用到上面的例子中也就是说在com.juvenxu.mvnbook.project-b项目中不应该实现了两个功能。最好的做法是为MySQL和PostgreSQL分别创建一个Maven项目,基于同样的groupId分配不同的artifactId,如:com.juvenxu.mvnbook:project-b-mysql和com.juvenxu.mvnbook:project-b-postgresql,在com.juvenxu.mvnbook:project-b-mysql实现对MySQL数据的持久化,在com.juvenxu.mvnbook:project-b-postgresql实现对postgresql数据库的持久化。

1.8 排除依赖

有时候会出现这样的情况,你的Hibernate依赖于Sun JTA API,但是因为版权原因,Sun JTA API并不在仓库中。而Apache Geronimo项目有一个对应的实现。这时你就可以排除Sun JAT API,再声明Geronimo的JTA API实现,见代码清单:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.juvenxu.mvnbook</groupId>
    <artifactId>project-a</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <dependency>
            <groupId>com.juvenxu.mvnbook</groupId>
            <artifactId>project-b</artifactId>
            <version>1.0.0</version>
            <exclusions>
                <exclusion>
                    <!-- 排除对project-c的依赖 -->
                    <groupId>com.juvenxu.mvnbook</groupId>
                    <artifactId>project-c</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.juvenxu.mvnbook</groupId>
            <artifactId>project-c</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
</project>

上述代码中,项目A依赖于项目B,但是由于一些原因,不想引入传递性依赖C,而是自己显示声明对于项目C1.0.0版本的依赖。代码中用exclusions元素可以包含多个exclusion子元素。需要注意的是声明exslusion的时候只需要groupId和artifactId,而不需要version元素。

1.9 归类依赖

当我们使用Spring Framework的依赖时,会有许多依赖,如:spring-core:2.5.6.org、org.springframework:spring-beans:2.5.6等,它们是来自同一项目下的不同模块。它们依赖的版本都是相同时,当需要升级Spring Framework时,这些依赖的版本都会一起升级。这时候我们可以用Maven属性的方式来定义一个名为springframework.version的属性,让所有的spring framework子模块都引用它。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.juvenxu.mvnbook</groupId>
    <artifactId>project-a</artifactId>
    <version>1.0.0</version>

    <properties>
        <springframework.version>3.1.2.RELEASE</springframework.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${springframework.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${springframework.version}</version>
        </dependency>
    </dependencies>
</project>

这样当我们需要升级Spring Framework的时候就只需要改属性值就可以了。

1.10 优化依赖

使用mvn dependency:list命令可以查看项目已解析的依赖

使用mvn dependency:tree命令可以查看项目构成的依赖树

使用mvn dependency:analyze命令可以分析当前项目的依赖

上一篇:《Maven实战》整理二:Maven的使用

下一篇:暂无

时间: 2024-08-01 15:57:12

《Maven实战》整理三:坐标和依赖的相关文章

Maven学习笔记之——坐标和依赖(上)

Maven学习笔记之--坐标和依赖(上) 1.    Maven坐标概念 Maven通过构件的坐标来在Maven仓库中定位到具体的构件.Maven的坐标元素包括groupId.artifactId.versiion.packaging.classifier.Maven内置了一个中央仓库地址.需要时Maven会根据坐标到其中下载.具体关于中央仓库的介绍在后面. 2.    Maven坐标详解 比如下面一组坐标: <groupId>org.andy.items</groupId> &l

maven详解之坐标与依赖

看着简单而又复杂的pom.xml文件,看似熟悉,当自己编写的时候觉得简单,但是看人家项目的时候又觉得复杂的很,现在我们一起来分析这个pom文件. Maven的坐标为各种构件引入了秩序,任何一个构件都必须明确的定义自己的坐标,maven的坐标包括如下的元素: groupId: 定义当前Maven项目隶属的实际项目 artifactId: 该元素定义实际项目中的一个Maven项目或模块 version: 该元素定义Maven项目当前所处的版本 packaging: 该元素定义Maven项目的打包方式

Maven学习笔记之——坐标和依赖(中)

Maven学习笔记之--坐标和依赖(中) 1.    传递性依赖 1.1    何为传递性依赖 项目中经常有引入一个jar包还要引入其他与其相关的jar包.自己搜的话要注意很多.比如版本问题等.而Maven会解析解析各个直接依赖的POM.将哪些必要的间接依赖以传递依赖的形式引入到项目中. 依赖范围不仅可以控制依赖与三种classpath关系.还对传递依赖产生影响. 假设A依赖B,B依赖C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖.第一直接依赖的范围和第二直接依赖

Maven学习笔记之——坐标和依赖(下)

Maven学习笔记之--坐标和依赖(下) 1.    最佳实践 归纳Maven依赖使用的常用技巧.方便用来避免和处理很多常见问题. 1.1.     排除依赖 传递性依赖会给项目隐式地引入很多依赖,这极大地简化了项目依赖的管理.但是有些时候这种特性也会带来问题.例如,当前项目有一个第三方依赖,而这个第三方依赖由于某些原因依赖了另外一个类库的SNAPSHOT版本,那么这个SNAPSHOT就会成为当前项目的传递性依赖,而SNAPSHOT的不稳定性会直接影响到当前的项目.这时候需要排除掉该SNAPSH

maven第5章坐标和依赖

5.5依赖范围 runtime:运行时依赖范围 举的例子是JDBC驱动实现,不理解? 5.6传递性依赖 5.7依赖调解 maven第5章坐标和依赖

Maven实战(三)Eclipse构建Maven项目(转)

转帖:http://www.iteye.com/topic/1123225 ? 1. 安装m2eclipse插件??? 要用Eclipse构建Maven项目,我们需要先安装meeclipse插件??? 点击eclipse菜单栏Help->Eclipse Marketplace搜索到插件Maven Integration for Eclipse 并点击安装即可,如下图: ?? 安装成成之后我们在Eclipse菜单栏中点击File->New->Other,在弹出的对话框中会看到如下图所示:

[Maven实战](9)传递性依赖

了解Spring的朋友都知道,创建一个Spring Framework项目都需要依赖什么样的Jar包.如果不使用Maven,那么在项目中就需要手动下载相关的依赖.由于Spring Framework又会依赖与其他开源类库,因此实际中往往会下载Spring Framework的jar包,还的下载所有它依赖的其他jar包.这么做往往就引入了很多不必要的依赖.另一种做法是只下载Spring Framework的jar包,不包含其他的相关依赖,到实际使用的时候,再根据报错信息,或者查询相关文档,加入需要

maven实战第三篇_03_yucong_项目的基本坐标groupId,artifactId和version

<groupId>com.yucong.commonmaven</groupId><artifactId>commonmaven</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging> <name>common_maven</name> 1 groupId 定义了项目属于哪个组,举个例子,如果你的公司

maven实战第十篇_10_yuocng_依赖

依赖调解: 项目A有这样的依赖关系:A -> B -> C -> X(1.0) , A -> D -> X(2.0) X是A的传递性依赖,但是两条依赖路径上有两个版本的X,那么哪个X会被Maven解析使用呢? 两个版本都解析是不对的,因为那会造成依赖重复,因此必须选择一个. maven依赖调解的第一原则是:路径最近者优先.该例子中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用. 但是,比如这样的依赖关系:A -> B  ->

转】Maven实战(三)---插件动态打包

原博文出于:http://blog.csdn.net/liutengteng130/article/details/41622013    感谢! maven把项目的构建划分为不同的生命周期(lifecycle),这个过程包括:编译.测试.打包.集成测试.验证.部署.maven中所有的执行动作(goal)都需要指明自己在这个过程中的执行位置,然后maven执行的时候,就依照过程的发展依次调用这些goal进行各种处理. 下面说一下在打包的时候遇到的问题: Maven在用插件动态打war包的时候出现