生产中常见的maven问题以及解决方法
1、maven依赖冲突问题
1.1、现象:
依赖冲突很经常是类包之间的间接依赖引起的。每个显式声明的类包都会依赖于一些其它的隐式类包,这些隐式的类包会被 maven 间接引入进来,从而造成类包冲突
1.2、步骤:
排查Maven冲突的步骤:
检查依赖树:使用mvn dependency:tree命令检查项目的依赖树,查看是否有冲突。
排除依赖:如果发现特定的依赖冲突,使用<exclusions>标签排除不需要的传递依赖。
版本管理:在pom.xml中使用<dependencyManagement>来统一管理版本。
清理工程:使用mvn clean清理之前的构建结果。
重新构建:执行mvn install重新构建项目。
检查插件版本:确保pom.xml中指定的Maven插件版本是正确的,避免由于插件版本不匹配导致的问题。
更新Maven:确保使用的是最新版本的Maven,有时候旧版本的Maven可能会导致一些不可预见的问题。
检查环境:确保本地Maven仓库的状态良好,网络连接没有问题,并且所有的环境变量都设置正确。
如果以上步骤无法解决问题,可以考虑寻求更具体的帮助,比如在Maven社区、Stack Overflow等平台上提问。
1.3、case:
1.3.1、检查冲突:
通过 dependency:tree
是命令来检查版本冲突如下
[INFO] org.example:hello:jar:1.0-SNAPSHOT
[INFO] ± org.springframework:spring-context:jar:5.2.7.RELEASE:compile
[INFO] | ± (org.springframework:spring-aop:jar:5.2.7.RELEASE:compile - omitted for conflict with 5.2.0.RELEASE)
[INFO] | ± org.springframework:spring-beans:jar:5.2.7.RELEASE:compile
[INFO] | | - (org.springframework:spring-core:jar:5.2.7.RELEASE:compile - omitted for duplicate)
[INFO] | ± org.springframework:spring-core:jar:5.2.7.RELEASE:compile
[INFO] | | - org.springframework:spring-jcl:jar:5.2.7.RELEASE:compile
[INFO] | - org.springframework:spring-expression:jar:5.2.7.RELEASE:compile
[INFO] | - (org.springframework:spring-core:jar:5.2.7.RELEASE:compile - omitted for duplicate)
[INFO] - org.springframework:spring-aop:jar:5.2.0.RELEASE:compile
[INFO] ± (org.springframework:spring-beans:jar:5.2.0.RELEASE:compile - omitted for conflict with 5.2.7.RELEASE)
[INFO] - (org.springframework:spring-core:jar:5.2.0.RELEASE:compile - omitted for conflict with 5.2.7.RELEASE)
omitted for duplicate 表示有 jar 包被重复依赖,最后写着 omitted for conflict with xxx 的,说明和别的 jar 包版本冲突了,而该行的 jar 包不会被引入。
比如上面有一行最后写着 omitted for conflict with 5.2.7.RELEASE,表示 spring-core 5.2.0 版本不会被项目引用,而 spring-core 5.2.7 版本会被项目引用
1.3.2、解决冲突:
项目的 pom.xml 形如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
通过查看依赖树,我们知道项目会引用 5.2.7.RELEASE 的 spring core jar 包,而不会引用 5.2.0 的 jar 包,如果我们想用 5.2.0 版本的 spring core 包,我们该如何做?
1.3.2.1、方法一
使用第一声明者优先原则
谁先定义的就用谁的传递依赖,即在 pom.xml 文件自上而下,先声明的 jar 坐标,就先引用该 jar 的传递依赖。因此我们如果要使用 5.2.0 版本的 spring core 包,我们可以改成如下声明:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
</dependencies>
然后查看依赖树mvn dependency:tree
:
[INFO] org.example:hello:jar:1.0-SNAPSHOT
[INFO] ± org.springframework:spring-aop:jar:5.2.0.RELEASE:compile
[INFO] | ± org.springframework:spring-beans:jar:5.2.0.RELEASE:compile
[INFO] | | - (org.springframework:spring-core:jar:5.2.0.RELEASE:compile - omitted for duplicate)
[INFO] | - org.springframework:spring-core:jar:5.2.0.RELEASE:compile
[INFO] | - org.springframework:spring-jcl:jar:5.2.0.RELEASE:compile
[INFO] - org.springframework:spring-context:jar:5.2.7.RELEASE:compile
[INFO] ± (org.springframework:spring-aop:jar:5.2.7.RELEASE:compile - omitted for conflict with 5.2.0.RELEASE)
[INFO] ± (org.springframework:spring-beans:jar:5.2.7.RELEASE:compile - omitted for conflict with 5.2.0.RELEASE)
[INFO] ± (org.springframework:spring-core:jar:5.2.7.RELEASE:compile - omitted for conflict with 5.2.0.RELEASE)
[INFO] - org.springframework:spring-expression:jar:5.2.7.RELEASE:compile
[INFO] - (org.springframework:spring-core:jar:5.2.7.RELEASE:compile - omitted for conflict with 5.2.0.RELEASE)
通过依赖树,我们可以看到项目已经引入 5.2.0 版本的 spring core 包。
1.3.2.2、方法二
**使用路径近者优先原则 **
即直接依赖级别高于传递依赖。因此我们可以在最先的 pom.xml 添加如下内容:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
然后通过工具查看如下,发现用的是5.2.0
1.3.2.3、方法三【推荐】
排除依赖
排除依赖的方式有两种,一种是通过maven的插件实现(百度一下就知道这里不做赘述。),第二种是在maven的配置文件中来搞;下面我们来主要说下这种方式,具体如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>spring-core</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
这种就实现了,对5.2.7的排除,使用的是5.2.0的;下图也可以佐证:
*** 版本锁定***
使用 dependencyManagement 进行版本锁定,dependencyManagement 可以统一管理项目的版本号,确保应用的各个项目的依赖和版本一致。
如果我们项目中只想使用 spring core 5.2.0 的包,pom.xml 可以改为如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
则 在导入spring-core的依赖的时候会自动导入5.2.0的版本;如下图:
注:dependencyManagement 只是声明依赖,并不自动实现引入,因此子项目需要显示的声明需要用的依赖
2、依赖管理&传递问题
2.1、问题
项目结构复杂存在多级多pom文件的时候,管理不好会导致依赖传递不下去或者是版本混乱等问题。
2.2、排查
查看maven依赖树+pom文件
maven依赖树查询命令:mvn dependency:tree
2.3、解决方案
-
新项目
- 在立项初期就要确认好 jdk、maven、spring | springboot | other…技术栈的版本,然后统一管理。
-
老项目
- 搞清楚maven的依赖管理逻辑,然后添加响应的依赖即可
-
maven依赖管理
1. dependencies:自动引入声明在dependencies里的所有依赖,并默认被所有的子项目继承。
如果项目中不写依赖项,则会从父项目继承(属性全部继承)声明在父项目dependencies里的依赖项。
2. dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要的依赖。
如果不在子项目中声明依赖,是不会从父项目中继承的;
只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;
如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
同时dependencyManagement让子项目引用依赖,而不用显示的列出版本号。
Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号,实现所有子项目使用的依赖项为同一版本。
3、maven install 或者是maven package报错
3.1 、问题
由于maven的pom文件或者引入包导致打包或者编译失败。如下场景:
3.2、排查
maven中对于问题排查,提供了一个参数 [X],在执行install或者package的时候可以加上,能够看出来我们的maven的具体配置参数,来检查。如:
mvn -X package
这行命令可以在执行package命令的时候,打印出来详情。
如下:
如上一样,其实已经打出来我们需要的信息,说明source&target 的版本是1.5的
那我们检查下maven的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mydatabase</artifactId>
<version>1.1</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>21</java.version>
<maven.compier.source>21</maven.compier.source>
<maven.compier.target>21</maven.compier.target>
</properties>
</project>
以上是我们的配置文件,发现文件里面是21,那我们想配置的其他例如编码都是ok的说明配置文件是生效的;那既然配置文件生效我们再来检查下对应的配置项发现compier
错了,我们改成compiler
试试。
3.3、解决方案
修改对应的配置,修改后为:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>mydatabase</artifactId>
<version>1.1</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>21</java.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
</project>
发现编译成功了:
我们在看看打印出来的日志:
看到日志内容也符合预期了,完美!