# 包的依赖管理

# 1. 坐标和 mvnrepository 网站

在 maven 中通过『坐标』概念来确定一个唯一确定的 jar 包。坐标的组成部分有:

元素 说明
<groupId> 定义当前 Maven 组织名称
<artifactId> 定义实际项目名称
<version> 定义当前项目的当前版本

任意两个不同包,它们的这三个属性必定至少有一项是不同的。

那么如何确定一个 java 包的坐标?通过 https://mvnrepository.com (opens new window)

mvnrepository 是一个与中央仓库对应的查询系统。在这里你可以查询你所需要的 java 包的坐标。

你只需要复制粘贴你所查到的 java 包的坐标 <repository> 片段即可。

# 2. 依赖管理

依赖管理』就是对项目中 jar 包的管理。可以在 pom.xml 文件中定义 jar 包的坐标,管理依赖。

整体结构(其它无关元素略)

project 
└── dependencies
    ├── dependency
    ├── dependency
    ├── ...
    └── dependency

主要包含如下元素:

<dependencies>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.25</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.25</version>
    </dependency>

    <dependency> ... </dependency>	
    <dependency> ... </dependency>	
    <dependency> ... </dependency>	

</dependencies>

maven 项目会『引用』你本地仓库中的这些 java 包。如果你本地仓库中没有,那么 maven 会先从网络仓库中下载这些 java 包到你的本地仓库。

这些 java 库在本地仓库的存放的路径规则是:

C:\Users\<用户名>\.m2\repository\<groupId>\<artifactId>\<version>

例如,上面的 slf4j-api 在本地仓库中的存放路径就是

C:\Users\59960549\.m2\repository\org\slf4j\slf4j-api\1.7.25

需要注意的是,maven 项目是在『引用』这些 java 库。在项目最终打包前,你的 maven 项目中并没有真正地『包含』这些 java 包,你会发现无论你『引用』多少 java 包,你的项目源码的文件夹大小实际上并没有增加(其中只有你的项目源码)。maven 就是通过这种方式来节约空间。

# 3. 依赖范围

依赖范围
(Scope)
对 main classpath 有效 对 test classpath 有效 打入包中 例子
compile Yes Yes Yes log4j
test - Yes - junit
privided Yes Yes - servlet-api
runtime - - Yes jdbc 驱动实现类

compile<scope> 的默认值。

# 4. 依赖传递

<dependencies>

  <!--
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
  </dependency>
  -->

  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.25</version>
  </dependency>

<dependencies>

前面的这个依赖可以将 slf4j-api 去掉,也依然可行。原因在于,虽然我们没有声明引用、使用 slf4j-api,但是我们声明要引用、使用的 slf4j-simple 它要使用 slf4j-api ,因此,maven 仍然还是会把 slf4j-api 纳入我们的项目中。

我们如何知道 slf4j-simple 它会使用 slf4j-api ?有两种方式:

  1. mvnrepository.com 中查询 slf4j-simple 时,该网站上会列出它所依赖的其它的包(如果有的话)

  2. 开发工具会有图形化界面让你能看到包的依赖关系,你可以直观地看到,slf4j-simple 依赖于 slf4j-api 。这样你可以对你的 repositories 进行优化。

# 5. 依赖冲突

依赖冲突指的是你的项目中『包含了同一个包的两个不同版本』。这种情况下通常会导致项目报错,或启动失败。

依赖冲突常见于两个场景:

  1. 同一个项目的两个开发人员不约而同想到使用同一个 java 库,而互相不知道。从而导致项目的 pom 中引入了同一个包的两个不同版本。

  2. 项目依赖于 A 和 B 两个库,看似没有问题,但是 B 库本身又依赖于 A 库(或者是,B 库依赖于 C 库,而 C 库依赖于 A 库)。从而导致项目中最终还是包含了 A 库的两个不同版本。

Maven 会自动解决依赖冲突问题,它基于两个原则来处理:

  1. 路径最近者优先

    项目 A 有如下的依赖关系:

    A -> B -> C -> X (1.0)

    A -> D -> X (2.0)

    maven 最终包含的会是 X 库的 2.0 版本。

  2. 路径相等,先声明者优先

    项目 A 有如下的依赖关系:

    A -> B -> Y(1.0)

    A -> C -> Y(2.0)

    若 pom 文件中 B 的依赖坐标先于 C 进行声明,则最终 Y 的版本为 1.0 。

在开发工具中,可以有图形化界面让你只管地看到依赖关系,其中会将依赖冲突展示出来。IDEA 图形化展示效果比 Eclipse 要更好。