maven in action(四)maven坐标和依赖

坐标(coordinate)

       数学课本中的坐标,在平面中的坐标(x,y)能标明平面中的一点,(x,y,z)能找到空间立体中的一点。根据你的身份证地址能找到这个世界上独一无二的你。而在maven中,世界上任何一个构建(jar或者war)都能用maven坐标唯一标识,maven坐标包括groupId、artifactId、version、packaging、classifier。我们提供正确的坐标元素,maven就能找到对应的构建。在上个maven helloworld示例中,我们可以看到pom.xml文件中对junit jar的坐标。

       当我们开发自己项目的时候,也需要为其定义适当的坐标,这是maven强制要求的。这样其他的maven项目才能够引用该项目生成的构建。


坐标详解

       maven坐标为各种构建引入了秩序,任何一个构建都必须明确定义自己的坐标,一组maven坐标是通过一些元素定义的它们是groupId、artifactiId、version、packaging、classifier。我们来看上一个helloworld项目中的对junit坐标定义。

<span style="font-size:14px;"><span style="font-family:Microsoft YaHei;"><dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies></span></span>

       groupId:定义maven项目隶属的实际项目。groupId一般定义到组织级别。也就是一个组织项目往往会被划分成很多的模块。

       artifactId:该元素定义实际项目中的一个maven项目(或者模块)。

       version:该元素定义mavne项目当前所处的版本。junit的版本是3.8.1。

       packaging:定义maven项目的打包方式。默认是jar。

       classifier:该元素用来帮助定义输出一些附属构建。附属构建与主构建对应,不能直接定义项目的classifier,因为附属构建不是项目直接生成的,而是由附加插件帮助生成的。

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

       之后我们就可以进行依赖管理了。

依赖配置

       如何配置项目对jar的依赖?

              <project>

                    ........

                     <dependencies>

                     <dependency>

                     <groupId></groupId>

                     <artifactId><artifactId>

                     <version></version>

                     <type></type>

                     <scope></scope>

                     <optional></optional>

                     <exclusions>

                            <exclusion>

                            .......

                            </exclusion>

                     </exclusions>


              <dependency>

                     ............

       </dependencies>

       </project>

       根据元素project 下的dependencies可以包含一个或者多个dependency元素,以声明一个或者多个项目依赖。每个依赖可以包含的元素有:

       groupId 、artifactId和version:依赖的基本坐标,对于任何一个依赖来说,基本的坐标都是最重要的,maven根据坐标才能找到需要的依赖。

       type:依赖的类型,对于项目坐标定义的packaging。大部分情况下,该元素不声明,默认为jar。

       scope:依赖的范围,见下一节--依赖范围。

       optional:标记依赖是否可选,见下面。

       exclusions:用来排除传递性依赖,见下节。



依赖范围

       依赖范围元素标签<scope>表示可选。我们在上篇中maven helloworld的文章中可以看到测试范围用元素scope表示。

       我们知道在编译源代码的时候,我们想要IDE为我们编译哪些代码,是需要我们告诉IDE的,像在myeclipse中需要设置build path,设置java build path。而maven在编译项目源代码的时候需要使用一套classpath,而maven在编译和执行测试的时候会使用另外一套classpath。无论是开发主代码还是测试代码,IDE都会为我们编译,而实际运行maven项目的时候,又会使用另外一套classpath。

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

       compile:编译依赖范围。

       对于编译、测试、运行三种classpath都有效。也就是在编译的时候,打包的时候,和部署的时候都会把该范围的依赖包打包进去。

       test:测试依赖范围。

       在测试范围有效,在编译和打包的时候都不会使用这个依赖。所以在打包后的jar中不包括junit jar的依赖。我们以看到生成war包中的web-info 下的lib文件夹中jar不包含junit jar的依赖。

       provided:已提供依赖范围。

       依赖在编译和测试过程有效,在运行的时候无效。典型的servlet-api,编译和测试项目的时候需要该依赖,但在运行的时候,由于容器已经提供,就不需要maven重复引入一遍。

       runtime:运行时的依赖范围。对于测试和运行class path有效,对于编译主代码的时候无效。

       system:系统依赖范围。与provided依赖范围完全一致。但是,使用system范围的依赖必须通过systemPath元素显式地指定依赖文件的路径。一般不使用。

总结如下图所示:

maven in action(四)maven坐标和依赖_第1张图片

传递性依赖

       传递性依赖和依赖范围

       什么是传递性依赖?例如我们的一个项目依赖于spring-core的依赖,而spring-core又依赖于commons-logging,并且spring-core和commons-logging均为compile依赖范围,这样我们的项目就间接的依赖了commons-logging。这就是传递性依赖,和数学中的概念一致。

       依赖的传递性也是受依赖的范围影响,依赖范围不同,可能依赖就不会传递,也可能间接依赖的范围不同。

假设我们的A->B->C ,A依赖于B是第一直接依赖,B依赖于C是第二直接依赖,A对于C是传递性依赖。如下图所示。

maven in action(四)maven坐标和依赖_第2张图片



依赖调解

       有时候我们的依赖会出现冲突,就像一个普通的web构建项目,有时候我们可能不小心引入了一个jar的不同版本,而在maven构建的项目中则为了解决了这个问题假如有这样的依赖,A-->B-->C->X(1.0)、A->D->X(2.0)。X是A的传递依赖,且依赖路径不同,依赖X项目出现重复,maven会怎么选择,A依赖于X的1.0版本还是2.0版本?maven项目一般会选择路径最近的优先,也就是就近原则,和英语中的就近原则有异曲同工之妙。




实际中我们需要

排除依赖

       当一个项目中通过传递依赖,依赖了一个第三方依赖,或者而是依赖了版本为SNAPSHOT(快照)版本,而快照版本是不稳定的,我们就要排除这个传递性依赖。

例如:

<span style="font-size:14px;"><dependency>
    <groupId>com.alibaba.external</groupId>
    <artifactId>sourceforge.spring</artifactId>
    <version>2.0.1</version>
    <exclusions>
        <exclusion>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency></span>
       上述配置依赖于spring包,spring又依赖于log4j的包,我们可以把这个依赖排除,在项目中显示引用log4j的依赖。

归类依赖

       就像数据中的归类总结,数学运算中的提取公因式等,这里的归类依赖原理也是这样的。

       提取一些“公因式”不仅让代码变得整洁,更重要的是可以避免重复,在修改的时候,只修改一次,降低了错误发生的概率。如下代码所示:

<span style="font-size:14px;"><properties>
  	<springframework.version>2.5.6</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>
   
  </dependencies></span>

       maven运行的时候会会将所有${springframework.version}替换成实际值2.5.6。


优化依赖

       使用dependency:list和dependency:tree 帮助我们详细了解项目中所有依赖的具体信息。
       使用dependency:analyze工具可以帮助分析当前项目的依赖。
analyze的结果中包含了两部分:
       Used undeclared dependencies:项目中使用但未显式声明的依赖。这种依赖意味着潜在的风险;
       Unused declared dependencise:项目中未使用的,但显式声明的依赖。对于这种依赖不能直接删除,因为analyze只会分析编译和测试需要的依赖,其他依赖它无法发现,因此需要仔细分析。

      下一篇:依赖的继承和聚合






你可能感兴趣的:(maven坐标和依赖)