前言

最近经常遇到知识星球中的小伙伴,问我一些关于Maven的问题。

说实话,Maven在我们日常开发中,使用的频率非常高。

今天这篇文章跟大家总结一下,使用Maven时一些最常见的坑,希望对你会有所帮助。

1.Maven核心原理

1.1 坐标体系

坐标冲突案例

<!-- 错误:同一artifactId声明两次 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>httpclient</artifactId> <!-- 同名不同组! -->
<version>1.0.0</version>
</dependency>

现象NoSuchMethodError 随机出现,因类加载器加载了错误Jar

1.2 依赖传递

依赖解析流程:

传递规则

  1. 最短路径优先:A→B→C→D(1.0) vs A→E→D(2.0) → 选择D(2.0)
  2. 第一声明优先:先声明的依赖版本胜出

1.3 生命周期

关键特性

  • 执行mvn install会自动触发从validateinstall的所有阶段
  • 插件绑定:每个阶段由具体插件实现(如compile阶段绑定maven-compiler-plugin

1.4 仓库体系

私服核心价值

  1. 缓存公共依赖 → 加速构建
  2. 托管内部二方包 → 安全隔离
  3. 控制依赖审批流 → 合规管控

2.Maven中最常见的坑

坑1:循环依赖

案例:订单模块order依赖支付模块payment,而payment又反向依赖order

报错[ERROR] A cycle was detected in the dependency graph

解决方案

  1. 抽取公共层:order-apiorder-core & payment-core
  2. 依赖倒置
// 在payment模块定义接口
public interface PaymentService {
void pay(Order order); // 参数用Order接口
} // order模块实现接口
public class OrderServiceImpl implements PaymentService {
// 实现逻辑
}

坑2:依赖冲突

典型场景:引入A、B两个组件

  • A依赖C:1.0
  • B依赖C:2.0

    → Maven按规则选择其一,导致另一方兼容性问题

定位工具

mvn dependency:tree -Dverbose

输出:

[INFO] com.example:demo:jar:1.0
[INFO] +- org.apache.httpcomponents:httpclient:jar:4.5.13:compile
[INFO] | \- commons-logging:commons-logging:jar:1.2:compile
[INFO] \- com.aliyun:oss-sdk:jar:2.0.0:compile
[INFO] \- commons-logging:commons-logging:jar:1.1.3:compile (版本冲突)

强制统一版本

<dependencyManagement>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version> <!-- 强制指定 -->
</dependency>
</dependencies>
</dependencyManagement>

坑3:快照依赖

错误配置

<dependency>
<groupId>com.internal</groupId>
<artifactId>core-utils</artifactId>
<version>1.0-SNAPSHOT</version> <!-- 快照版本! -->
</dependency>

风险:相同版本号可能对应不同内容,导致生产环境行为不一致

规范

  1. 生产发布:必须使用RELEASE(如1.0.0
  2. 内部联调:使用SNAPSHOT但需配合持续集成

坑4:依赖范围错误

误用案例

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>compile</scope> <!-- 应为provided -->
</dependency>

后果:Tomcat中运行时抛出java.lang.ClassCastException(容器已提供该包)

范围对照表

Scope 编译 测试 运行 典型用例
compile Spring Core
provided Servlet API
runtime JDBC驱动
test JUnit

坑5:资源过滤缺失

问题现象src/main/resources下的application.yml未替换变量:

db:
url: ${DB_URL} # 未被替换!

修复方案

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- 开启过滤 -->
</resource>
</resources>
</build>

同时需在pom.xml中定义变量:

<properties>
<DB_URL>jdbc:mysql://localhost:3306/test</DB_URL>
</properties>

坑6:插件版本过时

经典案例:JDK 17+项目使用旧版编译器插件

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version> <!-- 不支持JDK17 -->
</plugin>

报错Fatal error compiling: invalid target release: 17

升级方案

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>

坑7:多模块构建顺序

错误结构

parent-pom
├── user-service
├── payment-service # 依赖order-service
└── order-service

构建命令mvn clean install → 可能先构建payment-service失败

正确配置

<!-- parent-pom中声明构建顺序 -->
<modules>
<module>order-service</module>
<module>payment-service</module> <!-- 确保顺序 -->
<module>user-service</module>
</modules>

坑8:本地仓库污染

故障场景mvn clean install成功,同事却失败

根源:本地缓存了损坏的lastUpdated文件

清理方案

# 清除所有无效文件
find ~/.m2 -name "*.lastUpdated" -exec rm {} \; # 强制重新下载
mvn clean install -U

坑9:私服配置错误

慢如蜗牛的原因

  1. 中央仓库直连(国内访问慢)
  2. 镜像配置错误

优化配置settings.xml):

<mirrors>
<mirror>
<id>aliyun</id>
<name>Aliyun Maven Mirror</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf> <!-- 覆盖中央仓库 -->
</mirror>
</mirrors>

坑10:IDE与命令行行为不一致

典型分歧

  1. Eclipse能编译,命令行失败 → .projectpom.xml不一致
  2. IDEA运行正常,mvn test失败 → 测试资源未配置

统一方案

<!-- 显式配置测试资源 -->
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>

3.企业级最佳实践

依赖管理黄金法则

  1. 严格父POM:所有版本在父POM的<dependencyManagement>中锁定
  2. 持续检查:CI流水线加入依赖检查
mvn versions:display-dependency-updates
  1. 公私分明

    • 公开依赖 → 从阿里云镜像下载
    • 内部依赖 → 私服管控

高可用构建架构

总结

  1. 能用:会执行mvn clean install
  2. 会用:理解生命周期、解决依赖冲突
  3. 善用
    • 通过mvn dependency:analyze剔除无用依赖
    • 使用archetype生成标准化项目
    • 集成enforcer-plugin规范构建

Maven的本质不是工具约束,而是架构纪律

当你不再被构建失败打断思绪,当你的依赖树如水晶般透明,才算真正驯服了这只“构建巨兽”。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

本文收录于我的技术网站:http://www.susan.net.cn

Maven中的这些坑,99%的人不知道!的更多相关文章

  1. maven build和push image中遇到的坑(学习过程记录)

    最近在做jenkins的持续集成构建,其中一项是要实现docker容器化部署.项目本身是maven项目,我对于maven和docker都没有什么认知基础,于是求助百度和官网,从头开始啃起.遇到了不少的 ...

  2. maven中的snapshot来源与注意事项

    maven中的snapshot来源与注意事项 (2012-04-23 15:37:48) 转载▼ 标签: 杂谈 分类: java maven的依赖管理是基于版本管理的,在maven2之后,把版本管理细 ...

  3. 记 Maven 本地仓库埋坑之依赖包为何不能用

    记一次 Maven 本地仓库埋坑之 Verifying Availability 背景 某 Java 后端项目使用 maven 构建,因为某些原因,某些依赖库下载不了,直接找其它人索要了他电脑上的 m ...

  4. 理解Maven中的SNAPSHOT版本和正式版本

    Maven中建立的依赖管理方式基本已成为Java语言依赖管理的事实标准,Maven的替代者Gradle也基本沿用了Maven的依赖管理机制.在Maven依赖管理中,唯一标识一个依赖项是由该依赖项的三个 ...

  5. maven 中snapshot版本和release版本的区别

    maven中的仓库分为两种,snapshot快照仓库和release发布仓库.snapshot快照仓库用于保存开发过程中的不稳定版本,release正式仓库则是用来保存稳定的发行版本.定义一个组件/模 ...

  6. 整理iOS9适配中出现的坑(图文)

    原文: http://www.cnblogs.com/dsxniubility/p/4821184.html 整理iOS9适配中出现的坑(图文)   本文主要是说一些iOS9适配中出现的坑,如果只是要 ...

  7. IDEA使用maven中tomcat插件启动项目乱码问题

    今天用IDEA来启动项目,使用的是maven中的tomcat7插件,正常启动后,再页面操作新增或修改数据时,发生了诡异的事, 中文保存后全部乱码...顿时不淡定了,接着就开始排查原因 首先检查IDEA ...

  8. IntelliJ IDEA14.1中java项目Maven中没有配置JDK时的问题

    在IntelliJ IDEA 14.1中使用在java项目中使用Maven时当没有在Maven中配置JDK编译版本.源码版本时,IDEA将默认的编译版本.源码版本设置为jdk5. 在IDEA中Lang ...

  9. maven中snapshot快照库和release发布库的区别和作用

    在使用maven过程中,我们在开发阶段经常性的会有很多公共库处于不稳定状态,随时需要修改并发布,可能一天就要发布一次,遇到bug时,甚至一天要发布N次.我们知道,maven的依赖管理是基于版本管理的, ...

  10. 如何在maven中添加jar包

    Maven 中央仓库地址: 1. http://www.sonatype.org/nexus/ 2. http://mvnrepository.com/ (本人推荐仓库) 3. http://repo ...

随机推荐

  1. 集合流之“anyMatch”的应用【返回boolean类型】

    判断集合中是否存在"字符串",返回boolean类型 boolean isExit = allSku.stream().map(Product::getFeatureList) . ...

  2. 私有资产测绘&安全流水线Shovel

    私有资产测绘&安全流水线Shovel(Preview)发布 发布版本:Shovel-v0.1.7 当前项目发布版本 Shovel-v0.1.7(预览版) | 企业级资产测绘管理,开启资配漏补新 ...

  3. 剪枝在pytorch中是如何实现的?

    Pytorch中剪枝源码可参考: https://github.com/pytorch/pytorch/blob/master/torch/nn/utils/prune.py 可参考: pytorch ...

  4. PHP MD5强碰撞

    MD5强碰撞 搬得这个师傅的 https://www.cnblogs.com/kuaile1314/p/11968108.html 可以看到,使用了三个等号,这个时候PHP会先检查两边的变量类型,如果 ...

  5. python3里面实现将日志文件写入当前脚本运行的文本中

    在 Python3 中,可以使用 logging 模块来实现将日志写入本地文本文件中.下面是一个简单的示例代码: import logging # 配置 logging 模块 logging.basi ...

  6. IDEA 使用@Autowired提示Field injection is not recommended

    摘要:IDEA 使用@Autowired提示Field injection is not recommended问题的解决办法.   在使用@Autowired注解进行bean注入,完成自动装配的工作 ...

  7. 如何用Leangoo破解需求隔离与频繁变更的协作困局?

    作为一位经历过"需求文档满天飞.系统各自为战"的研发负责人,我深知团队在需求频繁变更时面临的痛点--信息割裂导致响应滞后.优先级混乱引发返工.协作低效拖慢交付节奏. 近期,我深度测 ...

  8. Django中的分页器以及手绘验证码

    一.分页器 1.分页器的好处 通过分页管理多条数据,可以美化界面并能提高查询效率 2.一般方式进行分页 def get_students(request): page = int(request.GE ...

  9. P5749 [IOI2019] 排列鞋子

    算是一种新思路吧. 题目要求我们求最少的对调次数,想到了什么?求逆序对个数,我们只需将原来的 \(S_i\) 数组转化一下,求其逆序对个数即可. 转化规则为:从头开始,对于每个还未被赋值的 \(S_i ...

  10. Java IO<4>Java io与装饰器模式

    Java io与装饰器模式 装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包 ...