目录

正文

  我们知道maven能帮我们管理jar包,那么它是怎么管理的呢?这篇文章我们来详细介绍下。

[回到顶部](#jumpDirectory)

1、什么是坐标?

①、数学中的坐标

  在平面上,使用 X 、Y 两个向量可以唯一的定位平面中的任何一个点

  在空间中,使用 X、Y、Z 三个向量可以唯一的定位空间中的任意一个点

②、Maven 中的坐标

  俗称 gav:使用下面三个向量子仓库中唯一定位一个 Maven 工程

  在项目中的 pom.xml 文件中,我们可以看到下面gav的定义:

  1、groupid:公司或组织域名倒序

  <groupid>com.locqi</groupid>

  2、artifactid:模块名,也是实际项目的名称

  <artifactid>helloMaven</artifactid>

  3、version:当前项目的版本

  <version>0.0.1-SNAPSHOT</version>
      
gav.png   

③、Maven 坐标和仓库,jar 包的关系

  什么是仓库,后面我们会详细讲解,现在你只需要知道是Maven 用来存放 jar 包的地方。

  那么依照上面定义的 gav,我们执行 mvn -install 命令,会出现什么情况呢?

  首先进入到我们第二篇博客Maven 的安装配置,通过 settings.xml 文件配置的仓库目录。

  将我们上面配置的 gav 向量组合起来就是目录:

  com/locqi/helloMaven/helloMaven-0.0.1-SNAPSHOT.jar

maven-gav.png

  其次,我们观察打出来的 jar 包:

  helloMaven-0.0.1-SNAPSHOT.jar

  也就是 artifactid-version.jar

[回到顶部](#jumpDirectory)

2、什么是依赖?

  什么是 依赖?相信有过一定开发经验的人知道,每当我们需要使用某个框架时,比如 SpringMVC,那么我们需要导入相应的 jar 包,但是手动导入包的时候,往往会漏掉几个 jar 包,那么在使用该框架的时候系统就会报错。那么我们就说导入的包与未导入的包存在依赖关系。而使用 Maven,我们只需要在 pom.xml 文件中进行相应的配置,它就会帮助我们自动管理 jar 包之间的依赖关系。那么它是怎么管理的呢,接下来我们会详细讲解。

[回到顶部](#jumpDirectory)

3、依赖的详细配置

  我们以 Junit 为例,在 pom.xml 文件中进行详细而完整的配置。 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<project>     
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<type>...</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
<groupId>...</groupId>
<artifactId>...</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

  ①、dependencies:一个 pom.xml 文件中只能存在一个这样的标签。用来管理依赖的总标签。

  ②、dependency:包含在dependencies标签中,可以有无数个,每一个表示一个依赖

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

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

  ⑤、scope:依赖的范围,默认值是 compile。后面会进行详解。

  ⑥、optional:标记依赖是否可选。

  ⑦、exclusions:用来排除传递性依赖,后面会进行详细介绍。

[回到顶部](#jumpDirectory)

4、依赖的范围 scope

  scope.png
  
  一般情况下,我们对前面三个依赖用的比较多。下面的主程序表示maven 目录结构 src/main/java.测试程序目录结构为:src/test/java

  1、compile 范围依赖

    对主程序是否有效:有效

    对测试程序是否有效:有效

    是否参与打包:参与

    是否参与部署:参与

    典型例子:log4j

    

  2、test 范围依赖

    对主程序是否有效:无效

    对测试程序是否有效:有效

    是否参与打包:不参与

    是否参与部署:不参与

    典型例子:Junit

  

  3、provided 范围依赖

    对主程序是否有效:有效

    对测试程序是否有效:有效

    是否参与打包:不参与

    是否参与部署:不参与

  典型例子:servlet-api.jar,一般在发布到 服务器中,比如 tomcat,服务器会自带 servlet-api.jar 包,所以provided 范围依赖只在编译测试有效。

   

  4、runtime 范围依赖:在测试、运行的时候依赖,在编译的时候不依赖。例如:JDBC驱动,项目代码只需要jdk提供的jdbc接口,只有在执行测试和运行项目的时候才需要实现jdbc的功能。

  

  接下来我们举几个例子在工程中实际去理解下:

  test 依赖和 compile 依赖的区别:

  ①、首先我们在 pom.xml 文件中配置,Junit 的 test 依赖
    
junit-scope.png
    
  ②、我们在主程序中去导入 Junit 的包,然后进行 mvn -compile 编译,很明显,test 范围的在主程序中无效,故编译会报错。
    
  我们在 src/main/java 包下新建 MavenTest.java,并导入 Junit 包

create-MavenTest-javafile.png  
    
  然后执行 mvn -compile 操作,如下图报错信息:

junit-scope.png
 
  ③、我们将 Junit 的依赖范围改为 compile,然后执行 mvn -compile

junit-scope.png

  发现 mvn -compile 没有报错了。

junit-scope.png

[回到顶部](#jumpDirectory)

5、依赖的传递

  比如我们创建三个Maven 工程,maven-first,maven-second以及maven-third,而third依赖于second,second又依赖于first,那么我们说 second 是 third 的第一直接依赖,first是second的第二直接依赖。而first是third的间接依赖。

maven-first-second-third.png

  依赖之间的传递如下图:第一列表示第一直接依赖,第一行表示第二直接依赖

first-second-third.png

  总结:
  1.当第二依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
  2.当第二直接依赖的范围是test的时候,依赖不会得以传递。
  3.当第二依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为 provided;
  4.当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递的依赖范围为runtime;
  
  我们这里举个例子来看:

  ①、第二依赖范围是 test

    Maven_first 的pom.xml 文件如下:
    
maven-first-pom.png

  然后Maven_second依赖Maven_fisrt,Maven_third依赖Maven-second,其pom.xml 文件如下:

  Maven_second的 pom.xml
  
maven-second-pom.png

  Maven_third的 pom.xml
  
maven-third-pom.png
    
  我们发现在 Maven_third和 Maven_second 都没有 Maven_first 引入的 Junit 包,正好符合上面总结的第二点:当第二直接依赖的范围是test的时候,依赖不会得以传递。
    
maven-third-pom.png

  ②、第二依赖范围是 compile

  如果我们将 Maven_first 的Junit 改为 compile,那么将会符合上面总结的第一点:当第二依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
  
maven_first-scope-is-compile.png

[回到顶部](#jumpDirectory)

6、依赖的排除

  如果我们在当前工程中引入了一个依赖是 A,而 A 又依赖了 B,那么 Maven 会自动将 A 依赖的 B 引入当前工程,但是个别情况下 B 有可能是一个不稳定版,或对当前工程有不良影响。这时我们可以在引入 A 的时候将 B 排除。

  比如:我们在Maven_first 中添加 spring-core,maven 会自动将 commons-logging添加进来,那么由于 Maven_second 是依赖 Maven_first 的,那么 Maven_second 中将存在 spring-core(自带了commons-logging),这时候我们不想要 commons-logging,那该怎么办呢?我们上面第二大点提到了:

1
exclusions:用来排除传递性依赖

  Maven_first 的 pom.xml 文件

maven-first-add-spring-core.png

  由于 Maven_second 依赖 Maven_first,故Maven_second 存在 spring-core 包

maven-first-and-second-has-spring-core-jar.png

  如何排除呢?我们在 Maven_second 的 pom.xml 文件中添加如下代码:

maven-second-pom-exclusions-jar.png

  再次查看工程:Maven_second 的 commons-logging 已经移除了

commons-logging-jar-exclusions.png

[回到顶部](#jumpDirectory)

7、依赖的冲突

  在maven中存在两种冲突方式:一种是跨pom文件的冲突,一种是同一个pom文件中的冲突。

  ①、跨 pom 文件,路径最短者优先。比如我们在 Maven_first 中的 Junit 是4.9版本的,Maven_second 中的 Junit 是4.8版本的,那么Maven_third 中的 Junit 将会是那个版本呢?

junit-4.8-jar-same.png

  由上图我们可以看出,由于 Maven_second 是 Maven_third 的直接依赖,明显相比于 Maven_first 路径要短,所以 Maven_third 的 Junit 版本与 Maven_second 保持一致。

  ②、同一个pom.xml 文件,先申明者优先。

commons-logging-version-is-1.2.png

  看 Maven_second 工程目录:

maven-second-commons-logg-ver-is-1.2.png

[回到顶部](#jumpDirectory)

8、可选依赖

  Optional标签标示该依赖是否可选,默认是false。
  可以理解为,如果为true,则表示该依赖不会传递下去,如果为false,则会传递下去。

optional-is-true.png

  我们是在 Maven_second 的 pom 文件中设定 Junit 不可传递,那么Maven_third 工程中将不会有来自 Maven_second 的 Junit 的传递。

optional-set-true-none-junitJAR.png

那么Maven的生命周期是怎样的呢?请继续阅读下一篇文章吧!

最后更新: 2018年09月19日 15:41

× 请我吃糖~
打赏二维码