Maven仓库机制与可执行Jar打包:从依赖解析到构建避坑 简介面向Maven初学者与Java构建开发者这份资料围绕Maven仓库概念、本地JAR包引入及可执行JAR打包三个核心主题用一个小型Eclipse工程示例说明依赖管理与构建配置要点。压缩包共6个文件资源类型为zip包括Eclipse项目配置project、classpath、prefs、pom.xml示例以及一个Java主类文件整体约5KB配置与源码分离便于逐项对照。已有2767人学习下载说明其在日常开发排障中被频繁参考。读者可借此理解本地仓库~/.m2/repository的目录组织掌握手工放置JAR并执行mvn install引入本地包的流程同时对比maven-jar-plugin、maven-assembly-plugin、maven-shade-plugin三种打包方式在可执行JAR生成上的差异快速迁移到实际项目构建中。1. Maven仓库机制先搞懂依赖从哪来Maven 这东西很多 Java 开发天天在用但真被问到你项目里的 jar 包到底从哪来先找哪个仓库找不到又会怎样不少人会卡壳。我这些年帮团队排查构建问题发现大半的坑都出在对仓库机制的理解上所以这篇从仓库说起再聊本地包引入和可执行 jar 的打包套路一条线串完。1.1 仓库分三层先对号入座Maven 的仓库体系可以理解成三层结构本地仓库、远程仓库私服、中央仓库。本地仓库就是你自己机器上的一个目录默认在用户目录/.m2/repository下。所有从远程下载的依赖都会缓存到这里项目构建时优先从这里找。你可以把它理解成电脑里的浏览器缓存第一次访问慢之后就快多了。远程仓库尤其是公司内部的 Nexus 或 Artifactory 这类私服扮演的是中转站保险柜的角色。开发部门把公共组件传到私服上整个团队的机器都能拉取没有必要每个人都去访问外网。外网访问不稳定的时候私服的价值就特别明显。中央仓库是 Maven 官方维护的公共仓库托管了绝大多数开源组件的 jar 包。它位于互联网上默认配置就能用但在国内访问速度往往不够理想所以大家都会配置阿里云等镜像来加速。镜像的本质就是换一个距离更近、带宽更大的下载源。三层之间是逐级查找的关系本地仓库 → 私服 → 中央仓库。本地没有就去私服拉私服没有再往外网拉。拉下来之后会缓存在本地下次直接用。1.2 配置文件里的关键节点与镜像加速Maven 的配置文件是settings.xml位置有两处全局配置在 Maven 安装目录下的conf/里用户配置在~/.m2/目录下。推荐优先修改用户配置因为它是当前账号私有的不会影响同一台机器上的其他用户。一个完整的settings.xml至少需要关注这几个节点settings localRepositoryD:/maven-repo/localRepository mirrors mirror idaliyun-public/id nameAliyun Public Mirror/name urlhttps://maven.aliyun.com/repository/public/url mirrorOf*/mirrorOf /mirror /mirrors profiles profile idjdk-17/id activation activeByDefaulttrue/activeByDefault /activation properties maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target /properties /profile /profiles /settingslocalRepository指定本地仓库路径默认值在用户目录下如果你的 C 盘紧张建议挪到别的盘。mirrorOf的值如果配成*表示所有远程仓库都走这个镜像配成central就只对中央仓库做镜像。多镜像配置时注意mirrorOf不要互相重叠否则后面的会被前面覆盖。JDK 版本属性在打包时常被忽略但它直接决定了编译产物运行的 Java 版本。如果本机 JDK 是 17而服务器上是 8不显式指定编译版本的话打出来的 class 文件大概率跑不起来。1.3 依赖搜索顺序与找不到依赖的根源Maven 寻找依赖时遵循一个固定顺序查本地仓库存在且版本匹配直接使用。本地没有检查是否配置了私服去私服拉取。私服也没有走中央仓库或镜像。全部找不到报Could not resolve dependencies错误。实际项目里依赖解析失败的常见原因有这几种依赖写错了 groupId、artifactId 或 version坐标对不上。本地仓库里有残留的.lastUpdated后缀文件这类文件是下载中断留下的标记Maven 看到它就不会再尝试重新下载。网络对中央仓库访问不稳定或者镜像配置错误。依赖传递时某个中间件在私服上没有导致链条断裂。排查思路很简单看到解析失败先去本地仓库对应路径下看看有没有.lastUpdated文件有就删掉整个目录再重新构建没有就去 Maven 中央仓库官网搜一下坐标核对是否拼写有误。这两步能解决八成的依赖问题。2. 引入本地Jar包三种方式对比与实操项目开发中总会遇到这种情况有个 jar 是内部工具包没有传到公司私服也没法上传到中央仓库但你的代码就必须依赖它。这时候就需要想办法把它引入到项目里。2.1 方式一mvn install-file把本地包装进本地仓库这是最推荐的方式本质是手动将 jar 安装到本地 Maven 仓库之后在pom.xml里像一个普通依赖一样引用即可。操作步骤如下mvn install:install-file \ -Dfile./libs/my-custom-tool.jar \ -DgroupIdcom.example \ -DartifactIdmy-custom-tool \ -Dversion1.0.0 \ -Dpackagingjar执行完毕后你会在本地仓库对应路径下看到com/example/my-custom-tool/1.0.0/目录。然后在项目 pom 里正常声明dependency groupIdcom.example/groupId artifactIdmy-custom-tool/artifactId version1.0.0/version /dependency这个方法的优势很直接与普通依赖无差别不需要改动构建的配置。缺点也同样明显——它只在你本机生效。团队成员如果没执行同样的命令他们的本地仓库里没有这个依赖一拉代码就报错。所以这个方式只适用于个人验证或临时使用团队协作的场景并不合适。2.2 方式二system scope的利与弊在 pom 里直接指定系统路径引用 jar看起来更省事dependency groupIdcom.example/groupId artifactIdmy-custom-tool/artifactId version1.0.0/version scopesystem/scope systemPath${project.basedir}/libs/my-custom-tool.jar/systemPath /dependency这种方式把 jar 存在项目的libs/目录下随项目一起走适合团队多人协作的场景。但它有一个埋得很深的问题systemscope 的依赖在打包时默认不会被包含进最终的产物。如果你用mvn package打出的 jar 扔到服务器上运行时会报ClassNotFoundException。另外很多云构建平台和容器化构建工具对systemscope 支持不友好构建容易出幺蛾子。还有一个隐含问题system依赖不会参与依赖冲突调节遇到版本传递问题更难排查。2.3 方式三搭建私有仓库Nexus统一管理当团队规模上来了本地 install 和 system scope 都显得不够正规。标准做法是部署一个 Nexus 私服把项目依赖统一推上去。流程大致是部署 Nexus 服务。在 Nexus 中创建一个 hosted 类型仓库比如thirdparty。上传本地 jar 包到该仓库。在settings.xml或 pom 中配置仓库地址。团队成员正常拉取依赖无需任何额外操作。Nexus 的界面操作不算复杂进入仓库视图后选择 Upload 组件填写 GAV 坐标并选文件即可。配置完成之后整个团队解析依赖的行为就统一了。这个方案前期需要一定的部署成本但对团队长期效率的提升非常明显。2.4 三种方式的对比与选型建议这三种方式没有绝对的优劣更多是适用场景不同。我按实际经验画了一张对比表方式生效范围打包兼容性团队协作适用场景mvn install-file仅本机好差个人临时验证system scope随项目差一般小项目、快速演示Nexus 私服全团队好好正常团队项目如果你是一个人做项目怎么方便怎么来。但到了团队协作阶段我建议尽早把依赖纳入私服管理省得哪天新同事拉完代码一脸懵这个依赖哪来的为什么我构建不了3. 多种方式打出可执行Jar包依赖问题解决之后就轮到打包了。这里可执行的意思是打出来的 jar 可以java -jar app.jar直接启动不需要再手动拼 Class-Path。实现方式主要有三种每种有各自的定位。3.1 方式一maven-jar-plugin 外部依赖目录Maven 默认打的 jar 里面只有项目自己的 class 和资源文件不包含第三方依赖。所以即使打包成功直接java -jar也会报NoClassDefFoundError。一种做法是用maven-jar-plugin定制 manifest把 Class-Path 指向外部依赖目录plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-jar-plugin/artifactId version3.4.1/version configuration archive manifest mainClasscom.example.MainApplication/mainClass addClasspathtrue/addClasspath classpathPrefixlib//classpathPrefix /manifest /archive /configuration /plugin然后再用maven-dependency-plugin把项目依赖复制到lib/目录plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-dependency-plugin/artifactId executions execution phasepackage/phase goals goalcopy-dependencies/goal /goals configuration outputDirectory${project.build.directory}/lib/outputDirectory /configuration /execution /executions /plugin最后部署的时候把 jar 和lib/目录放在同一个文件夹下就能正常启动。这种方式的优点是产物比较清爽依赖单独存放升级某个依赖时只需要替换 lib 下的 jar。缺点是部署时要多带一个目录不够单文件。3.2 方式二maven-shade-plugin打一个胖jar如果要追求单个 jar 文件搞定一切maven-shade-plugin是最常用的方案。它的原理是把所有依赖解压出来重新打包进同一个 jar 中。plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId version3.5.1/version executions execution phasepackage/phase goals goalshade/goal /goals configuration createDependencyReducedPomfalse/createDependencyReducedPom transformers transformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer mainClasscom.example.MainApplication/mainClass /transformer /transformers filters filter artifact*:*/artifact excludes excludeMETA-INF/*.SF/exclude excludeMETA-INF/*.DSA/exclude excludeMETA-INF/*.RSA/exclude /excludes /filter /filters /configuration /execution /executions /plugin这里有几个值得注意的细节。createDependencyReducedPom建议设成false防止生成的 pom 被替换导致 IDE 里依赖显示异常。filters里排除签名文件是必要的——很多依赖 jar 包带有 jar 签名合并时这些签名文件不排除的话打包过程会直接报SecurityException或者运行时提示Invalid signature file digest。shade 插件也不是没有问题。当多个依赖包含同名文件比如某些配置文件、SPI 文件、META-INF/services下的扩展描述时后合并进来的会覆盖先合并的可能导致功能缺失。这种情况需要配合ServicesResourceTransformer这类 transformer 来做合并。如果项目用到了 Spring Boot官方推荐直接用 Spring Boot 的打包插件它内置了更完善的资源合并策略比 shade 更省心。3.3 方式三maven-assembly-plugin生成zip发布包maven-assembly-plugin的能力不局限于 jar它可以把项目打成 zip、tar.gz 等格式的发布包里面可以包含 jar、依赖库、启动脚本、配置文件适合做完整部署包。plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-assembly-plugin/artifactId version3.7.1/version configuration descriptorRefs descriptorRefjar-with-dependencies/descriptorRef /descriptorRefs archive manifest mainClasscom.example.MainApplication/mainClass /manifest /archive /configuration executions execution idmake-assembly/id phasepackage/phase goals goalsingle/goal /goals /execution /executions /pluginjar-with-dependencies是插件自带的一个预定义描述符效果与 shade 类似。但它生成的 jar 名字会带-jar-with-dependencies后缀默认不会替换原 jar。assembly 更适合的场景是配合自定义描述符把启动脚本、配置文件、静态资源一起打包成发布产物。在 CICD 流程中这种方式更利于上线部署的标准化。3.4 manifest主类与Class-Path的底层逻辑不管用哪种插件最终都要配置Main-Class和Class-Path。Main-Class告诉 JVM 启动时找哪个类的 main 方法Class-Path告诉 JVM 运行时去哪找依赖类。打开一个可执行 jar 的META-INF/MANIFEST.MF文件你会看到类似这样的内容Manifest-Version: 1.0 Main-Class: com.example.MainApplication Class-Path: lib/dependency-a.jar lib/dependency-b.jarshade 和 assembly 之所以打出的 jar 能独立运行就是因为它们把依赖真正放进了 jar 里或者把依赖路径写进了 manifest。而 maven-jar-plugin 的第一种方式依赖还在外部目录靠的是 manifest 里的 Class-Path 来定位。理解了这一层遇到为什么这个 jar 能跑、那个 jar 跑不了的问题你就知道该往哪里排查了。4. 常见问题排查与避坑实录4.1 打出的jar运行报NoClassDefFoundError这是最经典的问题。现象是mvn package显示 BUILD SUCCESS但java -jar一跑就报错。原因基本就是上面说的打出的 jar 里没有依赖。处理思路分两步。第一如果用默认的 spring-boot-maven-plugin确认是否已经执行了repackage目标。Spring Boot 的打包插件和普通 jar 插件不一样必须在spring-boot-maven-plugin的 execution 里绑定repackage阶段而且该插件的mainClass要显式指定否则在部分场景下会找不到主类生成一个不可执行的原始 jar。第二如果你用 shade确认依赖有没有被错误地 exclude 掉检查dependency-reduced-pom.xml是否杀掉了关键依赖。4.2 jar包冲突与依赖版本覆盖Maven 的依赖仲裁策略简单直接依赖路径最近的优先深度相同时先声明的优先。这就导致一个常见问题——你的项目里有两个间接依赖分别引入了不同版本的同一个库实际打包进去的是最先声明的那个另一个版本的类特征没对齐运行时就报NoSuchMethodError或者ClassNotFoundException。排查工具最有效的是mvn dependency:tree一条命令就能看清完整依赖树。看到版本冲突时用exclusion排除掉不需要的传递依赖或者用dependencyManagement统一版本管理。这里一定要记住dependencyManagement只管版本不会引入依赖它只声明版本真正要引入还是得用dependencies声明依赖本身。4.3 IDEA与命令行环境不一致每个项目在 IDEA 里配的 Maven 可能是自己安装的设置里也可能勾选了Use Maven wrapper但你命令行用的那套 Maven 又是另外一份配置两边 settings.xml、本地仓库路径不同很容易出现 IDEA 中构建正常、命令行构建失败的情况。我个人的做法是在 IDEA 的 Maven 配置页里强制指定 Maven home path、User settings file 和 Local repository尽量和命令行保持完全一致。这样无论在 IDE 里还是终端里操作依赖解析和打包的行为都统一。另外一个容易踩的坑是 IDE 中 Build 按钮和mvn clean package的结果可能不同——如果项目配了 profileIDE 可能需要手动激活 profile 才会生效而命令行加参数就能指定。4.4 Maven构建时的编码与资源过滤问题中文乱码是个老生常谈但很容易进坑的问题。在 pom 里统一声明编码能避免很多奇怪的构建问题properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding project.reporting.outputEncodingUTF-8/project.reporting.outputEncoding /properties另外要留意资源过滤的坑。默认情况下 Maven 的 resource 插件不会处理src/main/resources里的特殊符号但如果你启用了资源过滤filteringtrue/filtering那xxx或${xxx}这类占位符会被尝试解析如果对应的属性不存在构建就会报错或者替换成空值。所以非必要别开 filtering开了就确保属性都有定义。4.5 多模块项目中的打包细节多模块项目里父模块的 packaging 通常声明为pom子模块才会打到 jar。Maven 的 reactor 模式会自动按依赖关系排序模块的构建顺序子模块依赖了另一个子模块时它会自动先构建依赖模块。但有一点要注意子模块之间出现循环依赖时Maven 会直接构建失败提示 cycle 错误。这种问题没有技巧必须调整模块设计。在多模块打包时根目录执行mvn clean install和mvn clean package的结果也不同。install会把构建产物安装到本地仓库方便本地其他项目引用package只在 target 目录生成产物。如果子模块之间有依赖关系只执行package可能无法解析到尚未安装的模块实际使用中如果遇到找不到父模块之类的报错优先考虑在父目录执行install。写在最后的实操习惯做 Java 后端这几年我对 Maven 最大的体会就是它本质是约定优于配置的工具大部分问题都出在我猜它应该这样和它实际就是这样的偏差上。遇到构建问题先看本地仓库再看依赖树最后看打包插件配置基本能解决绝大多数疑难杂症。最后分享一个我自己一直保留的习惯新建项目时在根目录放一个mvnwMaven Wrapper它能把 Maven 版本固定下来团队所有人用同一个版本构建可以省掉我这构建没问题啊之类的经典对话。别看这设置不起眼版本不一致导致的诡异问题一抓一大把。Maven 不是个多复杂的工具但把这套流程理清了构建这块能省下大量无意义的排查时间。本文还有配套的精品资源点击获取

相关新闻

最新新闻

STM32智能医疗输液点滴系统:嵌入式硬件与PID控制实践

STM32智能医疗输液点滴系统:嵌入式硬件与PID控制实践

我手里的这套STM32智能医疗输液点滴系统,算是我折腾过的嵌入式项目里比较有代表性的一个。它不光是单片机外设的堆砌,而是把传感器采集、电机控制、人机交互、通信协议这些嵌入式基本功串在了一起,正好能覆盖一个完整产品的核心链路。整个项目…

2026/9/8 15:05:14
Abaqus三维应力单元选型全解:从C3D8R到C3D20R的工程实践指南

Abaqus三维应力单元选型全解:从C3D8R到C3D20R的工程实践指南

在Abaqus里做结构仿真,迟早会撞上“三维应力单元怎么选”这个问题。我刚入行那年,拿着一个复杂的支架模型,随手用默认的C3D8R算了一版,结果一受弯就出事——位移跟手算差了快百分之三十,折腾了一天才明白是沙漏和锁定在…

2026/9/8 15:05:14
学术海报模板与Poster实战指南:从设计到现场交流

学术海报模板与Poster实战指南:从设计到现场交流

学术会议的Poster环节,看着是拿着张打印好的大图往墙上一贴就完事,实际上它是整个会议里信息密度最高、也最容易翻车的社交场景。我自己第一次参加国际会议时,做海报前觉得“不就是把论文浓缩一下嘛”,结果到了现场发现&#xff0…

2026/9/8 15:05:14
计算机二级WPS第2章选择题高频考点与避坑指南

计算机二级WPS第2章选择题高频考点与避坑指南

很多人在备考计算机二级(WPS Office)的时候,都会觉得第2章“创建与处理文档”像是送分题:不就是打开软件写几段字、调一下格式吗?可真到选择题题库里过一遍,才发现完全不是那么回事。这一章的选择题几乎都在…

2026/9/8 15:05:14
降低ai检测率免费的办法:4个改写技巧+免费额度,降aigc和查重双达标

降低ai检测率免费的办法:4个改写技巧+免费额度,降aigc和查重双达标

降低ai检测率免费的办法:4个改写技巧免费额度,降aigc和查重双达标 降低ai检测率免费的办法到底有没有?有,而且不止一条。去年毕业季我帮同门改一篇2.6万字的教育学论文,把免费路线从头到尾趟了一遍:手动改…

2026/9/8 15:05:14
Agent能力渐进式加载:Skill、Function Call与MCP按需注入的工程实践

Agent能力渐进式加载:Skill、Function Call与MCP按需注入的工程实践

先亮个结论:如果你的Agent项目还在用“启动时把所有工具一股脑塞进System Prompt”的方式,那做到后面会非常难受。我在做Agent平台的时候,技能文件越堆越多,function call定义膨胀到上百个,再加上要接外部MCP服务端&am…

2026/9/8 15:00:14