Skip to content

Maven 与私服:镜像、依赖冲突 ​

Maven 的坑集中在两处:镜像配错导致下载慢/失败,以及依赖冲突导致运行时才报 NoSuchMethodError。

一、settings.xml ​

xml
<!-- ~/.m2/settings.xml -->
<settings>
  <localRepository>/data/maven/repo</localRepository>

  <mirrors>
    <mirror>
      <id>nexus</id>
      <name>internal nexus</name>
      <url>http://nexus.internal:8081/repository/maven-public/</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>

  <servers>
    <server>
      <id>nexus-releases</id>
      <username>deploy</username>
      <password>{加密后的密码}</password>
    </server>
  </servers>

  <profiles>
    <profile>
      <id>jdk17</id>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      </properties>
    </profile>
  </profiles>
</settings>

<mirrorOf> 的写法:

值含义
central只代理 central
*代理所有仓库(包括私服自己,会导致死循环)
external:*所有外部仓库,但不代理 localhost 和内网
*,!nexus-releases全部代理,除了指定的

mirrorOf 写 * 且 url 指向私服

如果私服本身又去代理 central,某些配置下会形成请求回环,表现为下载卡死或 404。用 external:* 或显式排除。

另外:repositories 里配置的仓库也会被 mirror 拦截,所以只要 mirror 覆盖了 central,pom 里再加阿里云仓库是无效的。

密码加密:

bash
mvn --encrypt-master-password
mvn --encrypt-password
text
{jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ=}

把 master 密码放进 ~/.m2/settings-security.xml。

二、依赖冲突 ​

现象 ​

编译通过,运行时报:

text
java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(JLjava/lang/String;)V
java.lang.ClassNotFoundException: org.slf4j.impl.StaticLoggerBinder

定位 ​

bash
mvn dependency:tree -Dverbose -Dincludes=com.google.guava:guava
text
[INFO] com.example:order-api:jar:1.4.2
[INFO] +- com.example:common-utils:jar:1.0.0:compile
[INFO] |  \- (com.google.guava:guava:jar:19.0:compile - omitted for conflict with 32.1.3-jre)
[INFO] \- com.example:cache-client:jar:2.1.0:compile
[INFO]    \- com.google.guava:guava:jar:32.1.3-jre:compile

Maven 的调解规则:

  1. 最短路径优先(层级浅的赢)。
  2. 路径深度相同时,先声明的赢。
  3. dependencyManagement 里的版本强制生效。

19.0 被 omitted for conflict,实际用的是 32.1.3-jre。如果代码按 19.0 的 API 写,就会 NoSuchMethodError。

解决 ​

xml
<!-- 1. 排除传递依赖 -->
<dependency>
  <groupId>com.example</groupId>
  <artifactId>common-utils</artifactId>
  <version>1.0.0</version>
  <exclusions>
    <exclusion>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
    </exclusion>
  </exclusions>
</dependency>
xml
<!-- 2. 统一版本(推荐) -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>32.1.3-jre</version>
    </dependency>
  </dependencies>
</dependencyManagement>
bash
# 3. 看最终生效版本
mvn help:effective-pom | grep -A3 '<artifactId>guava</artifactId>'

# 4. 看整个依赖树
mvn dependency:tree -Dverbose > /tmp/tree.txt

Spring Boot 项目用 BOM 统一版本:

xml
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>3.3.5</version>
</parent>

BOM 之外要覆盖版本时,在 dependencyManagement 里显式声明。

三、常见冲突类型 ​

现象冲突对象排查
NoClassDefFoundError: org/slf4j/...多个 slf4j 绑定dependency:tree -Dincludes=org.slf4j
NoSuchMethodError同一库多版本-Dverbose 看 omitted
ClassCastException 在 JDK 内部类两个版本的同一 jar查 lib/ 下是否有重复
java.lang.LinkageError同名类被加载两次查 fat jar 里的 META-INF
UnsupportedClassVersionError编译 JDK 高于运行 JDK对齐 maven.compiler.release

打出的 fat jar 里检查重复:

bash
unzip -l target/app.jar | grep -c 'BOOT-INF/lib/'
unzip -l target/app.jar | grep 'guava'

四、私服 Nexus ​

bash
docker run -d --name nexus -p 8081:8081 \
  -v /data/nexus:/nexus-data \
  sonatype/nexus3:3.70.1
bash
docker logs nexus | grep -i 'admin'
text
-------------------------------------------------

Initial admin password is in /nexus-data/admin.password
-------------------------------------------------
bash
cat /data/nexus/admin.password
text
8f2c1e9a-7b3d-4c5e-8a0b-2c4d6e8f0a1b

三个仓库:

仓库类型用途
maven-releaseshosted正式版本
maven-snapshotshosted快照
maven-publicgroup聚合上面两个 + 代理 central

发布到私服:

xml
<distributionManagement>
  <repository>
    <id>nexus-releases</id>
    <url>http://nexus.internal:8081/repository/maven-releases/</url>
  </repository>
  <snapshotRepository>
    <id>nexus-snapshots</id>
    <url>http://nexus.internal:8081/repository/maven-snapshots/</url>
  </snapshotRepository>
</distributionManagement>
bash
mvn deploy -DskipTests

releases 版本不能覆盖

maven-releases 默认禁止重复发布同一版本,会报 400 Repository does not allow updating assets。

改版本号重发,或临时把仓库策略改成 "Allow redeploy"(不推荐,会破坏可追溯性)。

五、构建加速 ​

bash
# 跳过测试
mvn -B clean package -DskipTests

# 只跑指定测试
mvn -B test -Dtest=OrderServiceTest

# 并行构建
mvn -B clean package -T 1C        # 每核一个线程

# 离线
mvn -o clean package

# 只构建子模块及其依赖
mvn -pl order-api -am clean package

-T 1C 在模块多的项目里能省一半时间,但并行构建对插件有要求,出问题就退回串行。

六、踩过的坑 ​

现象根因处置
下载极慢/超时没配镜像mirrorOf central 指向私服
mirrorOf * 后私服包拉不到所有请求被劫持改 external:*
运行时 NoSuchMethodError依赖冲突dependency:tree -Dverbose
本地能跑、服务器不行本地仓库有旧版本清 ~/.m2/repository 对应目录重下
SNAPSHOT 拿到旧包本地缓存未更新mvn -U clean package
打包缺资源文件没配 <resources>检查 src/main/resources 与插件配置
时区导致测试失败构建机时区不同测试里显式指定时区
OutOfMemoryError 构建时Maven 堆太小export MAVEN_OPTS="-Xmx2g"