第一次用Spring Boot打包项目时,我盯着控制台里突然冒出来的.original文件发愣——这玩意儿是哪来的?后来发现,这就是spring-boot-maven-plugin的repackage目标在悄悄干活。普通Maven打包生成的jar就像个裸机操作系统,而经过repackage处理的fat jar则是预装好所有依赖的完整系统。
传统Java应用部署需要先安装Tomcat,再把war包扔进去。Spring Boot的聪明之处在于,它把服务器和代码打包成一个能直接java -jar的独立包。这背后的魔法师就是repackage,它会做三件关键事:
xxx.jar.originalxml复制<!-- 这是开启魔法的咒语 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 关键在这里 -->
</goals>
</execution>
</executions>
</plugin>
用解压工具打开repackage生成的jar包,你会看到这样的目录结构:
code复制BOOT-INF/
├── classes/ # 你的代码和配置文件
├── lib/ # 所有第三方依赖jar
META-INF/
└── MANIFEST.MF
org/
└── springframework/
└── boot/
└── loader/ # Spring Boot的启动加载器
最让我惊艳的是MANIFEST.MF的变化。普通jar包的入口是Main-Class,而Spring Boot的配置是这样的:
properties复制Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.example.MyApplication # 你的主类
这种双保险设计让JarLauncher先启动,再由它加载你的真实主类。就像火箭发射需要多级推进,第一级先把加载环境准备好,第二级才真正启动业务代码。
用mvn package直接打包:
demo.jar(约5MB)META-INF和项目class文件启用repackage后:
demo.jar(可能50MB)和demo.jar.original(5MB)BOOT-INF/lib变成了依赖库的集装箱普通jar启动:
bash复制java -cp demo.jar:dependency1.jar:dependency2.jar com.example.Main
fat jar启动:
bash复制java -jar demo.jar # 所有依赖已内置
曾经在服务器上部署传统Java应用时,光是处理依赖冲突就耗掉我半天时间。有了fat jar,再也不用担心ClassNotFoundException了——除非你漏打了依赖。
踩过最痛的坑:把fat jar安装到本地仓库后,其他项目引用时报ClassNotFoundException。原来fat jar的类路径结构特殊,解决方法有两个:
xml复制<configuration>
<classifier>exec</classifier> <!-- 生成demo-exec.jar -->
</configuration>
有次升级Spring Boot版本后打包失败,查了半天发现是插件版本没同步:
xml复制<!-- 错误示范 -->
<spring-boot.version>3.1.0</spring-boot.version>
<plugin>
<version>2.7.0</version> <!-- 版本不匹配 -->
</plugin>
<!-- 正确做法 -->
<plugin>
<version>${spring-boot.version}</version>
</plugin>
在微服务项目中,子模块打包经常遇到依赖找不到的问题。我的经验是:
repackagemainClassmvn install先安装依赖模块xml复制<!-- 子模块配置示例 -->
<plugin>
<configuration>
<mainClass>com.example.service.UserServiceApplication</mainClass>
</configuration>
</plugin>
有些依赖可能和容器环境冲突,可以用exclude:
xml复制<configuration>
<excludes>
<exclude>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-annotations-api</artifactId>
</exclude>
</excludes>
</configuration>
Docker镜像构建时,可以用分层技术减少镜像体积:
xml复制<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
这会生成layers.idx文件,把依赖按更新频率分层,利用Docker缓存机制加速构建。
当执行mvn package时,spring-boot-maven-plugin会拦截Maven默认的打包过程:
maven-jar-plugin生成标准jar(后重命名为.original)JarLauncher和相关加载器类MANIFEST.MF配置启动路径整个过程就像房屋改造:
JarLauncher是智能家居系统BOOT-INF/lib是嵌入式家具最近在排查一个类加载问题时,发现Spring Boot Loader的LaunchedURLClassLoader会优先加载fat jar内部的类。这种设计解释了为什么fat jar能隔离依赖冲突——它本质上创建了一个独立的类加载空间。