Maven 依赖

最简单的依赖

        依赖是使用Maven坐标来定位的,而Maven坐标主要由GAV(groupId, artifactId, version)构成。因此,使用任何一个依赖之间,你都需要知道它的Maven坐标,关于如何寻找Maven坐标,《搜索Maven仓库》 一文可以帮助你。


1.依赖元素

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${springframework.version}</version>
   <type>jar</type>
   <scope>compile</scope>
  </dependency>

    groupId,必选,实际隶属项目

    artifactId,必选,其中的模块

    version必选,版本号

    type可选,依赖类型,默认jar

    scope可选,依赖范围,默认compile

    optional可选,标记依赖是否可选,默认false

    exclusion可选,排除传递依赖性,默认空

2.依赖范围

    maven项目又三种classpath(编译,测试,运行)

    scope用来表示与classpath的关系,总共有五种:

        compile:编译,测试,运行(在编译/打包/测试的时候把这个依赖加进去)

        test:测试(在测试时有效,在编译和打包时都不会使用这个依赖; 且test是不会再传递的)

        provided:编译,测试(在编译和测试的时候,把这个依赖加进去,但是在打包的时候,不加(如容器中有))

        runtime:运行( 在编译的时候不依赖,但是在运行的时候依赖;)

        system:编译,测试,同provided,但必须指定systemPath,慎用

3.传递性依赖

当依赖级别(层次)相同的时候,取最先依赖的;

当依赖级别(层次)不同的时候,取级别最短的;

顾名思义,你懂的,但是传递的范围会发生改变,这个由maven自身处理,只要理解下即可

第一列为第一依赖,第二列为第二依赖,单元格为传递范围

compile test provided runtime
compile compile _ _ runtime
test test _ _ test
provided provided _ provided provided
runtime runtime _ _ runtime

4.依赖调解

    传递路径长度取最短原则,传递路径长度相等时,采取最先申明原则

5.可选依赖

        尽量少用,可选依赖不会被传递,需要显式申明。

        有时候我们不想让依赖传递,那么可配置该依赖为可选依赖,将元素optional设置为true即可,例如:

<dependency>
  <groupId>commons-logging</groupId> 
  <artifactId>commons-logging</artifactId> 
  <version>1.1.1</version> 
  <optional>true<optional>
</dependency>

那么依赖该项目的另以项目将不会得到此依赖的传递

6.排除依赖

    

        当我们引入第三方jar包的时候,难免会引入传递性依赖,有些时候这是好事,然而有些时候我们不需要其中的一些传递性依赖

比如上例中的项目,我们不想引入传递性依赖commons-logging,我们可以使用exclusions元素声明排除依赖,exclusions可以包含一个或者多个exclusion子元素,因此可以排除一个或者多个传递性依赖。需要注意的是,声明exclusions的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。换句话说,Maven解析后的依赖中,不可能出现groupId和artifactId相同,但是version不同的两个依赖。

 如下是一个排除依赖的例子:

<dependency> 
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>2.5.6</version>
     <exclusions>
           <exclusion> 
                <groupId>commons-logging</groupId>    
                <artifactId>commons-logging</artifactId>
           </exclusion>
     </exclusions>
</dependency>

  <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>

7.分类依赖/依赖归类

        随着项目的增大,你的依赖越来越多,比如说你依赖了一堆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依赖包都是同一个版本。

8.优化依赖

可概括为三个命令

mvn dependency:list

表示依赖列表,maven eclipse插件已经实现,有图形化显示,在pom.xml的dependencies页

Maven 依赖_第1张图片

mvn dependency:tree

表示依赖列表,maven eclipse插件已经实现,有图形化显示,在pom.xml的dependency hierarchy页

Maven 依赖_第2张图片

mvn dependency:analyze

查找出在编译和测试中未使用但显示声明的依赖

Maven 依赖_第3张图片

分类器(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 依赖)