
1. 为什么需要手动上传JAR到Nexus 3在Java生态的日常开发中我们早已习惯了Maven或Gradle的丝滑体验一行mvn deploy构建好的构件Artifact就自动发布到了Nexus仓库团队其他成员通过依赖坐标就能轻松引入。这种自动化流程是持续集成和高效协作的基石。然而在实际工作中总有一些“特殊情况”会打破这种自动化幻想迫使你不得不打开浏览器登录Nexus管理界面手动点击那个“Upload”按钮。我遇到过不少这样的场景。最常见的是处理遗留的、没有源码的第三方JAR包。比如某个业务系统依赖一个十年前的、由某个已离职同事编译的加密工具包你只有这个孤零零的legacy-utils-1.0.jar文件没有源码更没有POM文件。你无法通过构建工具发布它但新项目又必须依赖它。另一种情况是你需要将一个非标准的、不可执行的JAR比如一个仅包含工具类的库或者一个被打包成JAR的资源包发布到仓库供其他模块引用。标准的maven-jar-plugin或spring-boot-maven-plugin用于打可执行JAR生成的元数据可能不符合你的需求你需要完全自定义打包逻辑并手动控制上传过程。手动上传的核心价值在于“掌控力”。它绕过了构建工具的默认流程允许你将任何符合JAR格式的文件按照你指定的坐标GroupId, ArtifactId, Version和分类直接置入仓库的特定位置。这对于统一管理所有形式的二进制依赖、搭建企业内部完整的制品库至关重要。Nexus 3的界面化上传功能虽然直观但其中涉及仓库类型选择、坐标规范、文件分类等细节一步错就可能导致依赖解析失败。接下来我将结合多次实操经验带你走通从准备JAR文件到在项目中成功引用的完整链路并重点拆解那些容易踩坑的环节。2. 上传前的核心准备理解仓库与构件坐标在点击上传按钮之前有两个概念必须彻底厘清它们决定了你的JAR文件最终被放置在Nexus的哪个“地址”以及项目如何通过Maven找到它。2.1 选择正确的目标仓库Hosted RepositoryNexus 3中的仓库主要分为Proxy代理远程仓库、Hosted托管本地仓库和Group仓库组。手动上传我们的目标一定是某个Hosted仓库。通常企业内部会建立诸如maven-releases和maven-snapshots的Hosted仓库分别用于存放正式版本和快照版本构件。这是最基础的分类。注意务必根据你JAR文件的版本号后缀来选择仓库。如果你的版本号以-SNAPSHOT结尾例如1.0.0-SNAPSHOT必须上传到Snapshots仓库如maven-snapshots。如果是不带-SNAPSHOT的正式版本如1.0.0则必须上传到Releases仓库如maven-releases。传错仓库会导致Maven无法解析该依赖因为Maven在请求时会根据版本号后缀自动路由到对应的仓库路径进行查找。除了版本约定另一个关键点是仓库的“策略”。在Nexus仓库配置中有一个Deployment Policy选项通常为Allow redeploy、Disable redeploy或Read-only。对于Releases仓库为了保持制品的不可变性Immutable生产环境通常设置为Disable redeploy这意味着同一个版本号的构件上传一次后就不能再次覆盖上传。如果你在上传后发现有文件传错需要覆盖可能需要管理员临时调整策略或删除原有构件。这是一个常见的权限坑。2.2 构件的“身份证”GAV坐标与PackagingMaven通过一组称为GAV的坐标来唯一标识一个构件手动上传时必须明确指定GroupId: 通常代表组织或项目组使用反向域名格式如com.company.team。ArtifactId: 项目的名称或模块名如>project groupIdcom.yourcompany.core/groupId artifactIdcustom-utils/artifactId version2.1.0/version packagingjar/packaging build plugins !-- 核心打包插件 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-jar-plugin/artifactId version3.3.0/version configuration !-- 关键不指定archivemanifestmainClass即是不可执行JAR -- !-- 可以添加其他清单属性如Implementation-Version -- archive manifest addDefaultImplementationEntriestrue/addDefaultImplementationEntries /manifest /archive /configuration /plugin !-- 可选生成源码JAR -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-source-plugin/artifactId version3.3.0/version executions execution idattach-sources/id phaseverify/phase goals goaljar-no-fork/goal /goals /execution /executions /plugin /plugins /build /project执行mvn clean package后在target目录下会生成custom-utils-2.1.0.jar。你可以用解压工具打开它查看META-INF/MANIFEST.MF文件里面不会有Main-Class这一行。这个JAR就是标准的、作为依赖库的不可执行JAR。4.2 手动组装与上传非Maven项目JAR对于非Maven项目例如一个纯Ant项目或手工编译的项目产生的JAR你缺少了最重要的pom.xml。当你手动上传这个JAR到Nexus时Nexus会为你生成一个最简单的POM。但有时这个自动生成的POM可能缺少真实的依赖声明导致下游项目在编译时出现类找不到的错误尽管运行时可能因为依赖传递而正常。解决方案是手动创建一个最小化的POM文件并一同上传。为你手头的external-library-1.0.jar编写一个简单的pom.xml?xml version1.0 encodingUTF-8? project modelVersion4.0.0/modelVersion groupIdcom.external.vendor/groupId artifactIdexternal-library/artifactId version1.0/version packagingjar/packaging !-- 如果知道它的依赖在这里声明 -- dependencies dependency groupIdcom.google.guava/groupId artifactIdguava/artifactId version31.1-jre/version /dependency /dependencies /project在Nexus上传界面在“选择文件”部分除了选择JAR文件再点击一次“选择文件”或“添加更多资产”选择你刚创建的pom.xml。在上传表单中Group/Artifact/Version填写与POM文件中一致的内容。Nexus会识别出你同时上传了POM文件并优先使用你提供的这个POM而不是自动生成一个空的。这对于维护依赖传递链的完整性非常关键。4.3 与可执行JAR的区分上传策略这是另一个实战中高频出现的场景一个Spring Boot项目需要发布。常规的maven-deploy-plugin配合spring-boot-maven-plugin默认只会部署可执行JAR即repackaged的fat jar。但其他项目如果想依赖这个Boot项目中的某个工具类它们需要的是那个原始的、不可执行的“薄”JAR。配置方案是在pom.xml中同时生成并部署两种JARbuild plugins !-- 1. 默认的maven-jar-plugin会生成不可执行的库JAR -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-jar-plugin/artifactId executions execution goals goaljar/goal /goals /execution /executions /plugin !-- 2. spring-boot-maven-plugin生成可执行JAR -- plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration !-- 关键配置为可执行JAR指定分类器 -- classifierexec/classifier !-- 确保不会替换掉默认JAR -- attachtrue/attach /configuration executions execution goals goalrepackage/goal !-- 这个goal会生成可执行JAR -- /goals /execution /executions /plugin /plugins /build执行mvn deploy后仓库里会有两个文件your-app-1.0.0.jar(不可执行主构件)your-app-1.0.0-exec.jar(可执行分类器为exec的附加构件)如果你想手动上传这两个JAR流程如下上传主构件不可执行JAR在Nexus界面Group/Artifact/Version填好Extension填jarClassifier留空选择your-app-1.0.0.jar文件上传。上传可执行构件不要关闭页面或重新开始。在同一个上传事务中或者新开一个Group/Artifact/Version填写完全相同的值Extension仍填jar但Classifier必须填exec然后选择your-app-1.0.0-exec.jar文件上传。这样在Maven依赖中如果需要引用可执行包虽然很少见可以通过classifierexec/classifier来指定。5. 上传后的依赖引用与常见问题排查上传成功只是第一步让其他Maven项目能正确引用到这个依赖才是最终目的。5.1 在项目中添加依赖在另一个项目的pom.xml中添加如下依赖dependency groupIdcom.yourcompany.core/groupId artifactIdcustom-utils/artifactId version2.1.0/version !-- 如果引用的是带分类器的构件如可执行JAR则需要此标签 -- !-- classifierexec/classifier -- /dependency同时确保项目的pom.xml或父POM中已经正确配置了指向你公司Nexus仓库的repositories和distributionManagement对于发布或镜像设置。5.2 依赖解析失败完整排查链路如果Maven无法下载你刚上传的依赖可以按照以下链路逐步排查这是我解决此类问题的标准思路第一步检查本地命令输出在项目根目录执行mvn clean compile -U-U强制更新快照。观察错误信息。错误1: “Could not find artifact ...”: 说明Maven在配置的仓库里根本找不到这个GAV坐标的构件。排查点1仓库地址。确认settings.xml中配置的仓库URL或镜像URL确实指向了你上传的Nexus实例和具体的仓库如maven-releases。排查点2版本号与仓库匹配。确认你依赖的版本号如2.1.0和你实际上传到Releases仓库的版本号完全一致包括大小写Maven坐标是大小写敏感的。确认你没有错误地将正式版本传到了Snapshots仓库。错误2: “... but failed to download the pom ...”: 找到了JAR但找不到对应的POM文件。排查点立刻去Nexus浏览界面查看该构件目录下是否存在.pom文件。对于手动上传如果只上传了JARNexus会自动生成POM。如果自动生成失败或被你误删就会报此错。手动补传一个POM文件即可。第二步在Nexus中直接搜索验证在Nexus管理界面的搜索栏顶部导航栏通常有放大镜图标使用“Component Search”输入完整的GroupId、ArtifactId和Version进行搜索。如果能搜到点击进入详情查看“Assets”标签页。这里会列出该构件的所有文件JAR, POM等。确认文件齐全并且文件名完全符合Maven路径规则。第三步检查仓库成员资格与顺序如果你的公司使用仓库组Repository Group例如一个名为maven-public的组它包含了maven-central、maven-releases、maven-snapshots等。你需要确认你上传的仓库如maven-releases是这个仓库组的成员。仓库组中成员的顺序。Maven会按顺序在组内仓库查找。确保你的内部仓库如maven-releases在公共代理仓库如maven-central之前。否则Maven可能会先去中央仓库查找你的com.yourcompany.core:custom-utils:2.1.0显然找不到然后报错而不会继续查找你内部的仓库。第四步清理本地缓存并重试Maven会将下载的构件缓存在本地仓库默认在~/.m2/repository。有时缓存会损坏或状态不一致。你可以直接删除本地仓库中对应路径的目录例如~/.m2/repository/com/yourcompany/core/custom-utils/2.1.0/然后重新执行mvn compile强制Maven从远程仓库重新下载所有文件。这是一个非常有效的“重启大法”。5.3 关于SNAPSHOT版本的特殊处理如果你上传的是-SNAPSHOT版本例如2.1.0-SNAPSHOTMaven对其处理有特殊逻辑。每次构建时Maven都会尝试检查远程仓库是否有更新的SNAPSHOT。在Nexus 3中SNAPSHOT仓库的实际存储路径会包含一个时间戳和构建号如custom-utils-2.1.0-20240521.063720-1.jar并通过maven-metadata.xml来管理哪个是最新的。手动上传SNAPSHOT时Nexus也会帮你处理这部分元数据。但需要注意的是手动上传的SNAPSHOT构件其“最新”的状态是由上传时间决定的。如果你多次上传同一个SNAPSHOT版本Nexus会更新元数据指向最新上传的那个带时间戳的构件。对于依赖方除非使用-U参数否则Maven可能仍然使用本地缓存的旧版本元数据。在涉及SNAPSHOT的协作中沟通和明确的版本管理策略比技术操作更重要。