Maven基础

核心思想:约定大于配置

其实约定大于配置这一思想在开发中经常出现
当程序中的某一个或者某一些参数没有配置的时候,程序会自动设定一个默认值。
这个默认值的设定,就称之为约定。

意义在于:减少不必要的配置。

仓库

Maven的仓库分为三种:

  1. 本地仓库:maven优先从本地库中获取依赖
  2. 中央仓库:当本地库没有所需依赖包时,中央仓库 → 本地仓库
  3. 远程私服仓库:中央仓库有非常多常用的lib,但并非所有,为了下载中央仓库下载不到的lib,自己构建私人的远程仓库以供下载构建依赖

标准目录

目录 作用
$ 项目根目录,存放pom.xml和所有子目录
${basedir}/src/main/java 存放 java 源代码
${basedir}/src/main/resources 存放资源文件,如 propert 文件
${basedir}/src/test/java 存放测试代码
${basedir}/src/test/resources 存放测试的资源文件
${basedir}/src/main/webapp/WEB-INF web 应用文件目录,如 web.xml,本地图片,jsp
${basedir}/target 打包输出目录
${basedir}/target/classes 编译输出目录
${basedir}/target/test-classes 测试编译输出目录

构件

  • groupId:当前Maven构件隶属组织名(必须)
  • artifactId:项目的唯一标识符,项目的名称(必须)
  • version:当前版本(必须)
  • scope:依赖范围指定
  • packaging:打包方式
  • classifier:通常用于区分以上项目都相同的构件的不同版本

依赖范围

依赖范围是三种可见性的组合
有三种可见性,编译,测试,运行

  • compile:默认的依赖范围,对于编译,测试,运行三种都生效

  • test:仅对于测试生效,典型的就是 Junit

  • provided:对于编译,测试生效。比如 servlet-api.jar 在 Tomcat 中已经提供了,只需要编译与测试阶段提供即可。

  • runtime:对于测试,运行生效。如JDBC驱动实现。
    注:由于现在的开发多使用mybatis,所以JDBC的部分不需要在编译阶段处理。

  • system:与上述均不相同。使用 system 后必须通过 systemPath 元素指定依赖文件的路径,不依赖 Maven 仓库的解析,所以存在依赖关系不可移植的风险。

依赖冲突

直接依赖冲突

众所周知,对于 Maven 而言,同一个groupId,artifactId下,只能使用一个 version

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.3.0</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.0</version>
    </dependency>
</dependencies>

对于上述配置,将使用3.5.0的mybatis,可以理解为后配置覆盖前配置(覆盖策略)

※思考问题

现在,我们可以思考下,比如工程中需要引入A、B,而 A 依赖 1.0 版本的 C,B 依赖 2.0 版本的 C,那么问题来了,C 使用的版本将由引入A、B的顺序而定? 这显然不靠谱!如果 A 的依赖写在 B 的依赖后面,将意味着最后引入的是 1.0 版本的 C,很可能在运行阶段出现类(ClassNotFoundException)、方法(NoSuchMethodError)找不到的错误(因为B使用的是高版本的C)!

传递依赖冲突

Maven 引入的传递性依赖机制,能大大简化依赖管理。大部分情况只需要关心项目的直接依赖,而不用关心依赖的依赖。但是当多个直接依赖的依赖出现冲突时,问题就令人抓狂了起来。

依赖传递有两种情况:

  1. 存在模块间的继承关系,在继承父模块后同时引入父模块中的依赖,可通过可选依赖机制放弃依赖传递到子模块
  2. 引入lib时附带引入该lib的依赖lib。这是依赖冲突的主要原因。如下所示。
    x → y → z(1.0)
    x → g → z(2.0)
    由于z 1.0和2.0两个都解析会导致依赖重复,所以必须选择其中一个。

依赖优化

Maven有一定的智能,可以对部分依赖进行调整来保证构件的唯一性。

因为只针对部分依赖,所以还是有误判的情况。因此,通过手动配置来优化依赖还是有必要的。
我们可以使用 maven-dependency-plugin 提供的三个目标来实现依赖分析

$ mvn dependency:list
$ mvn dependency:tree
$ mvn dependency:analyze

若需要更精细的分析,可以在命令后添加如下参数

-Dverbose
-Dincludes=<groupId>:<artifactId>

依赖冲突调解规则

软件开发依赖冲突问题,有四种原则:

  • 路径最短优先原则
  • 声明顺序优先原则
  • 排除原则
  • 版本锁定原则

依赖调解详解

Maven依赖调解遵循以下两大原则:路径最短优先声明顺序优先

第一原则:路径最短优先

若依赖链的长度不同是,优先使用短的

a --> b --> x(1.1)		// dist(a->x) = 2
a --> c --> d --> x(1.2)	// dist(a->x) = 3

上述情况,优先使用版本x(1.1)的依赖

第二原则:声明顺序优先

若依赖链的长度相同时,使用优先声明的

a --> b --> x(1.1)	// dist(a->x) = 2
a --> c --> x(1.2)	// dist(a->x) = 2

当 a 的 pom.xml 中优先声明 b,使用 x(1.1) 的依赖

解决依赖冲突

简单暴力,在对应pom.xml中加入一下内容即可

<dependency>
  <groupId>org.glassfish.jersey.containers</groupId>
  <artifactId>jersey-container-grizzly2-http</artifactId>
  <!-- 剔除依赖 -->
  <exclusions>
    <exclusion>
      <groupId>org.glassfish.hk2.external</groupId>
      <artifactId>jakarta.inject</artifactId>
    </exclusion>
    ...
  </exclusions>
</dependency>
posted @ 2022-07-18 17:28  spoonb  阅读(52)  评论(0编辑  收藏  举报