maven基础篇(2)-maven坐标和依赖

        下面pom.xml文件是运行mvn archetype:generate这个目标自动生成的HelloWorld入门maven项目的项目对象模型文件,如下:

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0modelVersion>

  <groupId>com.mycompany.helloworldgroupId>
  <artifactId>helloworldartifactId>
  <version>1.0-SNAPSHOTversion>
  <packaging>jarpackaging>

  <name>helloworldname>
  <url>http://maven.apache.orgurl>

  <properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
  properties>

  <dependencies>
    <dependency>
      <groupId>junitgroupId>
      <artifactId>junitartifactId>
      <version>3.8.1version>
      <scope>testscope>
    dependency>
  dependencies>
project>

一,maven坐标

1,依赖jar加载过程
        首先要明确,在pom.xml中定义了很多依赖。在项目启动后的不同的阶段,maven首先会去本地仓库根据maven坐标寻找这些jar并根据依赖的scope来决定不同的阶段加载这些依赖jar进类路径。如果本地仓库没有寻找到,maven会根据坐标去maven中央仓库寻找并联网下载这些jar进入本地类路径。

2,坐标
        不论是本地仓库,还是中央仓库,都会存在很多的构件,比如jar和war等等的。在如此众多的构件中,就是通过groupId,artifactId,version,packaging和classifier来唯一标识一个构件,也就是maven坐标。先来了解一下各个坐标元素的情况:

  • groupId:必需的,指示隶属的实际项目。通常用反写公司域名+具体项目名。大家都知道,谷歌公司有很多域名,其中一个最出名的则为google.com。而谷歌公司旗下又有很多对应各个具体项目的二级域名,比如谷歌学术scholar.google.com和谷歌地图maps.google.com。把这些二级域名反过来写,com.google.maps和com.google.scholar则对应maven的坐标元素groupId。说白了,这个坐标元素是唯一标识一个具体项目用的。
  • artifactId:必需的,指某个项目下的具体子项目(或子模块)。通常用“具体项目名-模块名”。大家都熟悉的spring这个框架,它是一个大项目,这个大项目根据具体功用划分为很多模块,比如spring-core,spring-context等等很多,这很多则对应maven的坐标元素artifactId。这个坐标元素唯一标识一个具体项目的具体模块(或者子项目也可以)。打包时生成的构建,则通常用“artifactId-version.jar”来表示。
  • version:必需的。构件的版本号。通常默认为一个具体值0.0.1-SNAPSHOT
  • packaging:打包方式。maven项目默认打包为jar。
  • classifier:定义构件的一些输出附属构件,比如javadoc和resources。spring-core-xx-javadoc.jar和spring-core-xx-resources.jar则为spring-core-xx.jar的附属构件。通常我们并不需要写这个,附属构件通常由一些maven插件来完成。
    maven基础篇(2)-maven坐标和依赖_第1张图片

        现在已经很清楚maven坐标是怎么回事了吧,要特别注意项目和依赖等所有构件在maven中都是由这几个坐标元素所唯一标识。


二,maven依赖和依赖范围scope

maven基础篇(2)-maven坐标和依赖_第2张图片

1,依赖范围scope

        先来看看maven项目构建的过程是怎样的,这里引用了别的博客的图:
maven基础篇(2)-maven坐标和依赖_第3张图片

        由上图可知,maven构建项目的主要过程是编译,测试和部署运行。而每一个过程是需要依赖一些jar才能完成的,这个大家都知道,java源代码不可能裸奔,必须有一些基础的类库才行。但是java是怎么能够找到这些类库或者说依赖的jar来支持每一步的运行呢?浅显的理解就是,JVM会首先去检查系统的环境变量,去类路径classpath下去找这些jar。所以我们必需把项目不同运行时期需要的jar等构件放入类路径。

        特别要注意,并不是所有的构件一旦加载进类路径就一直存在于类路径的,因为maven不同的运行时期时加载进类路径的一些构件,这些只是暂存其中的,也把暂存一些构件的类路径称为临时类路径。每个运行时期一旦结束这个临时类路径里面的构件就会被清除。这个对于maven来说,则为依赖范围scope,这个依赖范围是可以被配置的,也就决定了构件在类路径存在的时间范围。

        maven项目的主要过程:编译,测试和运行。根据上面分析可知,不同的过程classpath中的构件情况是不一样的。而maven依赖范围则主要讲maven主要三个过程中系统类路径classpath中依赖有无的情况。

        maven依赖范围

  • compile:编译依赖范围,默认情况。表示在编译测试运行三个过程classpath中都有此依赖,也就是此依赖什么时候都可以被找到并运行。
  • test:测试依赖范围。只和测试代码的编译运行有关,在测试代码的这两个过程中,依赖存在于classpath。别的过程都没有此依赖。
  • provided:提供依赖范围。编译和测试的时候,该依赖存在于classpath。运行时这个pom.xml中定义的这个依赖不存在于classpath。比如配置在pom.xml中的servlet-api.jar在编译测试时都有,但是运行时则不存在于classpath。但是并不影响运行,在运行时服务器会提供此依赖。
  • runtime:运行时依赖范围。测试和运行过程,依赖存在于classpath。但是编译时则不存在classpath。这里,我也用书上的例子来说明,比如我们写的JDBC代码我们都知道写的主要是统一的接口。编译的时候也只是在编译这些接口。只有当我们的项目真正测试和运行时,我们的JDBC才连接到了具体的数据库,也就是在此时具体厂商的JDBC驱动才被加载进classpath,以来支持我们的使用。
  • system:系统依赖范围。这个谨慎使用,这里不予讲解。
  • import:导入依赖范围。这个不会影响三个过程的依赖。后面会详细讲解。

三,依赖传递

1,何为依赖传递?

        maven基础篇(2)-maven坐标和依赖_第4张图片

        不知道你是否仔细看过本地仓库的一些依赖情况。本地仓库中的每一个依赖的同一个目录下面还有一个相关的pom.xml,这个pom.xml配置的则为这个依赖所需要依赖的一些构件。当你的项目中需要引入这个依赖时,这个依赖会被加载进类路径。并且,这个依赖相关的pom.xml配置的依赖也会被加载进类路径。这就形成了依赖的传递性,也就是依赖的依赖也被加载进类路径。特别要注意,在你的项目中,有些依赖的依赖会被用到,但是有些依赖的依赖并不会被用到。所以我们需要把这些不需要的依赖的依赖去除,也就是不让它被加载进本地仓库,更不让它加载进类路径。

2,依赖范围机制对依赖传递的影响

        maven的依赖范围机制不仅决定了依赖在编译测试运行三个过程在classpath存在情况,还会对依赖之间的传递性产生影响。

        maven基础篇(2)-maven坐标和依赖_第5张图片

        这个依赖之间的情况如上图,是由第一直接依赖的依赖范围和第二直接依赖的依赖范围决定着传递依赖的依赖范围,也就是决定着你的项目在三个过程在类路径是否存在依赖的依赖。情况如下,该图截自《Maven实战》:

        maven基础篇(2)-maven坐标和依赖_第6张图片

3,传递性依赖两个解决原则

        原则一:路径最近(短)者优先。
        maven基础篇(2)-maven坐标和依赖_第7张图片
        原则一:第一声明者优先。
        maven基础篇(2)-maven坐标和依赖_第8张图片

        特别注意,由于这两个原则,在项目的类路径classpath下,只可能存在两个版本依赖的其中一个。

4,依赖可选性optional

        书上这样的一个专业术语,差点没搞懂,我宁愿反过来来理解。在pom.xml中对某个依赖的optional设置为true,则这个依赖对于这个pom.xml所属的项目为可选性依赖。举个例子,比如我们的持久层可以采用多种数据库Mysql和Oracle等来实现,我们可以把这两个数据库的JDBC驱动都在pom.xml配置并且把optional设置为true,那么这两种驱动jar对于本项目来说则为可选性的依赖,选择哪个都可以,但是一次只能选择一个。特别要注意,此时两个驱动依赖都会被加载进类路径,也就是可选依赖只会当前项目产生影响。如下:

    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.10version>
        <optional>trueoptional>
    dependency>
    <dependency>
        <groupId>postgresqlgroupId>
        <artifactId>postgresqlartifactId>
        <version>8.4-701.jdbc3version>
        <optional>trueoptional>
    dependency>

        maven基础篇(2)-maven坐标和依赖_第9张图片

        如上图,A的第一直接依赖为B,C和D为B的可选性依赖。特别注意,由于可选依赖只对当前项目产生影响,则在A项目或者依赖A所在的项目运行时,并不会进入类路径。如果在A项目或者依赖A所在的项目中需要C或者D,则还需要在其pom.xml进行配置。

5,maven依赖的最佳实践

  • 如果传递性依赖如果在项目中不需要,可以用exclusion解除这个传递性依赖。但是要注意,在写exclusion子元素时只需要填写groupId和artifactId,因为从上文传递性依赖的两个解决原则中可知,类路径中不可能存在两个goupId和artifactId相同但是version版本不同的依赖。示例如下:
<dependency>
        <groupId>agroupId>
        <artifactId>a-m1artifactId>
        <version>2.4version>
        <exclusions>
            <exclusion>
                <groupId>bgroupId>
                <artifactId>b-m1artifactId>
            exclusion>
        exclusions>
    dependency>
  • 1,如果传递性依赖为SNAPSHOT不稳定版本,则在项目中直接声明该依赖的稳定性版本。由上文路径最短者优先原则可知,因为直接声明了该依赖的稳定版本,所以maven会把路径更长的传递性依赖去除。2,如果第一直接依赖为不稳定版本,直接换成稳定版本。

  • 把版本相同的很多依赖归类。例如spring项目有很多的项目模块,每个版本的项目spring,它的所有的项目模块的版本也就一样。可以在pom.xml利用全局属性元素properties先定义好spring项目版本号,然后下面的很多spring模块再引用即可。实例如下:

    <properties>
        <springframework.version>2.5.6springframework.version>
    properties>

   <dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
            <version>{springframework.version}version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-beansartifactId>
            <version>{springframework.version}version>
        dependency>
    <dependencies>
  • 优化依赖。最终进入类路径的,就称为已解析依赖。可以利用mvc dependency:list或者mvc dependency:tree来查看这些依赖和依赖树的情况。把项目直接用到的依赖直接设置在pom.xml,也就是成为顶层依赖,顶层依赖直接需要的依赖设置为第二层依赖……..把本项目不需要的传递性依赖,直接删除。

个人声明:本博文是在学习中,根据自己的理解所写。由于个人理解和专业素养有限,望指教,多谢!


参考资料:
1,《Maven实战》
2,《Apache Maven 入门篇》(系列教程)
3,《官方文档》
4,《maven可选依赖和依赖排除》

你可能感兴趣的:(maven,Maven学习基础篇)