Maven基础
核心思想:约定大于配置
其实约定大于配置这一思想在开发中经常出现
当程序中的某一个或者某一些参数没有配置的时候,程序会自动设定一个默认值。
这个默认值的设定,就称之为约定。意义在于:减少不必要的配置。
仓库
Maven的仓库分为三种:
- 本地仓库:maven优先从本地库中获取依赖
- 中央仓库:当本地库没有所需依赖包时,中央仓库 → 本地仓库
- 远程私服仓库:中央仓库有非常多常用的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 引入的传递性依赖机制,能大大简化依赖管理。大部分情况只需要关心项目的直接依赖,而不用关心依赖的依赖。但是当多个直接依赖的依赖出现冲突时,问题就令人抓狂了起来。
依赖传递有两种情况:
- 存在模块间的继承关系,在继承父模块后同时引入父模块中的依赖,可通过可选依赖机制放弃依赖传递到子模块
- 引入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>
本文来自博客园,作者:spoonb,转载请注明原文链接:https://www.cnblogs.com/spoonb/p/16491270.html
个人主页:blogcafe.cn 比博客园更新速度更快,欢迎大家的光顾