Skip to content

依赖管理

Maven 的依赖管理能够帮助开发人员自动解决软件包依赖问题,使得开发人员能够轻松地将其他开发人员开发的模块或第三方框架集成到自己的应用程序或模块中,避免出现版本冲突和依赖缺失等问题

依赖配置

依赖是 Maven中最关键的部分,如果我们想要在工程中引入某个jar 包,只需要在 pom.xml 中引入其 jar包的坐标即可。

xml
<project>
    ...
    <dependencies>
        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...</optional>
            <exclusions>
                <exclusion>
                    <groupId>...</groupId>
            		<artifactId>...</artifactId>
                </exclusion>
                ...
            </exclusions>
        </dependency>
        ...
    </dependencies>
    ...
</project>

大部分依赖声明只需要dependency包含groupId、artifactId和version这三个指定基本坐标的元素;而在一些特殊情况下,其它元素至关重要。

dependency包含:

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

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

  3. scope:依赖的范围,这个内容就比较多一点;

  4. optional:标记依赖是否可选;

  5. exclusions:用来排除传递性依赖

依赖范围

<scope>
  1. compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的maven依赖,对于编译 测试 运行三种的classpath都有效。

  2. test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试的classpath有效,在编译主代码或者运行主代码的时候都无法依赖此类依赖。典型的例子是jUnit,它只有在编译测试代码及运行测试代码的时候才有效。

  3. provided:以提供依赖范围。使用此依赖范围的maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行的时候,由于容器已经提供,就不需要maven重复地引入一遍。打包的时候可以不用包进去,别的设施会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是打包阶段做了exclude操作

  4. runtime:运行时依赖范围。使用此依赖范围的maven依赖,对于测试和运行classpath有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要jdk提供的jdbc的接口,只有在执行测试或者运行测试的时候才需要实现上述接口的jdbc的驱动

  5. system:系统依赖范围。从参与度来说,和provided相同,不过被依赖项不会从maven仓库下载,而是从本地文件系统拿。需要添加systemPath的属性来定义路径,该依赖与三种范围的classpath,和provided依赖范围完全一致。可能造成不可移植,谨慎使用。

  6. import:导入依赖范围。该依赖范围不会对三种classpath产生实际影响。只有在dependencyManagement下才有效果。

作用范围

  • 主程序范围有效(main文件夹范围)
  • 测试程序范围有效(test文件夹范围)
  • 是否参与打包(package指令范围)

image-20220731014259057

依赖范围编译classpath测试classpath运行classpath例子
compileYesYesYescommons-lang
testYesjunit
providedYesYesJservlet-api
runtimeYesJDBC-connector
systemYesYes本地的,maven仓库之外的类库文件

可选依赖

<optional>

对外隐藏当前所依赖的资源——不透明

排除依赖

xml
<exclusions>
   <exclusion>
       <groupId>...</groupId>
        <artifactId>...</artifactId>
   </exclusion>
   ...
</exclusions>

声明exclusion的时候只需要groupId和artifactId,而不需要version元素

依赖传递

在传递性依赖中,假设A依赖B,B依赖C,我们就说A与B是直接依赖,B与C是间接依赖,C对于A是传递性依赖。

image-20220731001105922

作用

  • 简化依赖导入
  • 确保版本一致

依赖传递

  • 直接依赖:在当前项目中通过依赖配置建立的依赖关系
  • 间接依赖:资源的资源如果依赖其他资源,当前项目间接依赖其他资源

带有依赖范围的资源在进行传递时,作用范围将受影响

image-20220731015902334

传递原则

在A依赖B,B依赖C,C能否传递到A,取决B依赖C时使用的依赖范围以及配置

传递可能性

  • B依赖C时使用compile范围,可以传递
  • B依赖C时使用test、provided范围,不可以传递
  • B依赖C时使用,不可以传递

依赖冲突

依赖冲突原则

  • 路径最短优先:当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高

    A—>B—>C—>D—>E—>X(version 0.0.1)

    A—>F—>X(version 0.0.2)

    则A依赖于X(version 0.0.2)。

  • 声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的

    A—>E—>X(version 0.0.1)

    A—>F—>X(version 0.0.2)

    则A依赖于X(version 0.0.1)。

  • 特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置的

依赖冲突解决

手动使用依赖排除,使用别人的依赖

exclusion

不可见,主动取消依赖传递

optional

依赖继承

继承是指在 Maven 的项目中,让一个项目从另一个项目中继承配置信息的机制。继承可以让我们在多个项目中共享同一配置信息,简化项目的管理和维护工作。

作用

  1. 简化依赖管理
    • 在父项目中定义一次依赖,所有子项目都可以自动继承这些依赖,无需在每个子项目的 POM 文件中都重新声明。
    • 这避免了依赖版本冲突的可能性,因为所有子项目都使用父项目中定义的相同依赖版本。
  2. 集中管理
    • 所有的依赖配置都集中在父项目的 POM 文件中,使得整个项目的依赖结构更加清晰和易于管理。
    • 如果需要更新某个依赖的版本,只需在父项目中更改一次即可,无需遍历所有的子项目并进行更改。
  3. 一致性
    • 通过继承,确保所有的子项目都使用相同版本的依赖库,从而减少了由于依赖版本不一致导致的问题。
  4. 减少 POM 文件的冗余
    • 如果没有依赖继承,每个子项目的 POM 文件都需要包含完整的依赖配置,这会导致大量的冗余代码。通过继承,可以大大减少 POM 文件的冗余内容。
  5. 易于扩展和维护
    • 父项目可以作为一个模板或基础,用于创建新的子项目。这些新的子项目将自动继承父项目的依赖配置,从而简化了项目的创建过程。
    • 在父项目中定义的依赖配置可以轻松地应用到所有子项目中,这有助于维护项目的整体结构和一致性。
  6. 自定义和覆盖
    • 虽然子项目会自动继承父项目的依赖配置,但子项目仍然可以覆盖或添加额外的依赖。这允许子项目根据自身的需求进行定制。
  7. 多模块项目的支持
    • Maven 支持多模块项目,其中每个模块都是一个独立的 Maven 项目。通过依赖继承,父项目可以集中管理所有模块的依赖配置,从而简化了多模块项目的管理。

语法

父工程

xml
  <groupId>com.atguigu.maven</groupId>
  <artifactId>pro03-maven-parent</artifactId>
  <version>1.0-SNAPSHOT</version>
  <!-- 打包方式必须是 pom -->
  <packaging>pom</packaging>

子工程

xml
<!-- 使用parent标签指定当前工程的父工程 -->
<parent>
  <!-- 父工程的坐标 -->
  <groupId>com.lcy</groupId>
  <artifactId>maven-parent</artifactId>
  <version>1.0-SNAPSHOT</version>
</parent>

<!-- 子工程的坐标 -->
<!-- 如果子工程坐标中的groupId和version与父工程一致,那么可以省略 -->
<artifactId>com.lcy</artifactId>
<!-- <version>1.0-SNAPSHOT</version> -->

依赖统一管理

父工程声明版本

使用dependencyManagement标签配置对依赖的管理

注意:被管理的依赖并没有真正被引入到工程

xml
<dependencyManagement>
        <dependencies>
           <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>${commons-lang3.version}</version>
            </dependency>
		<dependencies>
<dependencyManagement>

版本统一管理

xml
<properties>
	  <commons-lang3.version>3.3.2</commons-lang3.version>
</properties>

子工程

子工程引用父工程中的依赖信息时,可以把版本号去掉

把版本号去掉就表示子工程中这个依赖的版本由父工程决定

具体来说是由父工程的dependencyManagement来决定

xml
<dependencies>
	<dependency>
		<groupId>com.supermap.online</groupId>
		<artifactId>server-common</artifactId>
	</dependency>
<dependencies>

聚合项目

聚合(也称为多模块项目或多Maven项目)是指将多个项目组织到一个父级项目中,以便一起构建和管理的机制。聚合可以帮助我们更好地管理一组相关的子项目,同时简化它们的构建和部署过程。

作用

  1. 模块化开发
    • 将一个大型项目拆分成多个模块(或子项目),每个模块都有自己的职责和生命周期。
    • 这使得开发人员可以并行工作在不同的模块上,提高了开发效率。
    • 每个模块可以有自己的依赖关系、配置文件和构建过程。
  2. 依赖管理
    • Maven通过其依赖管理机制,可以自动处理模块之间的依赖关系。
    • 如果一个模块依赖于另一个模块,Maven会自动下载并包含所需的依赖项。
    • 这减少了手动管理依赖项的需要,降低了出错的可能性。
  3. 构建一致性
    • 在聚合项目中,所有模块都使用相同的构建生命周期和构建脚本(通常是pom.xml文件)。
    • 这确保了所有模块都使用相同的构建工具和构建配置,从而提高了构建过程的一致性和可维护性。
  4. 版本控制
    • 可以在聚合项目的根pom.xml文件中定义公共的依赖项版本,以确保所有模块都使用相同版本的库。
    • 当需要更新库版本时,只需在根pom.xml文件中进行更改,而无需手动更新每个模块的pom.xml文件。
  5. 简化部署
    • Maven提供了许多插件和工具,可以简化聚合项目的部署过程。
    • 例如,可以使用Maven的mvn deploy命令将多个模块一起部署到远程仓库或服务器。
  6. 易于扩展和维护
    • 随着项目的增长和变化,可以轻松地添加、删除或修改模块。
    • 由于每个模块都是独立的,因此可以单独进行版本控制和发布。
    • 这使得大型项目更易于管理和维护。
  7. 统一构建和测试
    • 在聚合项目中,可以使用Maven的命令行工具或持续集成(CI)服务器来统一构建和测试所有模块。
    • 这确保了所有模块都经过了相同的构建和测试过程,从而提高了代码的质量和可靠性。

语法

父项目中包含的子项目列表

xml
<project>
  <groupId>com.example</groupId>
  <artifactId>parent-project</artifactId>
  <packaging>pom</packaging>
  <version>1.0.0</version>
  <modules>
    <module>child-project1</module>
    <module>child-project2</module>
  </modules>
</project>