FEATURED · 精选文章

IDEA+Maven 打 Jar 包:两种方式与常见报错排查

发布时间 / 2026/9/18 15:34:54
来源 / 创域科博编辑部
栏目 / 资讯中心
IDEA+Maven 打 Jar 包:两种方式与常见报错排查 上周同事老张卡在一个再普通不过的需求上——把手里那个写了三个月的 Maven 项目打成 jar 包发给运维部署。结果他折腾了一下午先是mvn package之后双击跑不起来提示no main manifest attribute换了个方式重新打又报ClassNotFoundException一路怀疑人生。其实这事说穿了就那么点门道IDEA 配合 Maven 打 jar 包往大了说有一堆插件组合往小了说日常真正好用的就两种路子。这篇文章我想把这两种方式掰开揉碎讲清楚包括它们各自的适用场景、坑在哪、验证怎么做以及我这些年踩出来的实操经验。不管你是刚接触 Maven 打包的新手还是平时只用 IDE 一键打包、没深究过原理的老手看完都能对着自己的项目直接抄作业。1. 先搞清楚 Maven 打包到底在打什么很多人用 IDEA 用了很久Maven 面板里的clean、package、install点了无数次但问起来「打出来的 jar 里到底有啥」往往答不上来。这个问题不搞明白后面选哪种打包方式就是瞎蒙。1.1 一个 jar 包里通常装着哪几样东西jar 本质上就是个 zip 压缩包只不过换了个扩展名并且约定了一个META-INF/MANIFEST.MF文件来记录元信息。一个普通 Maven 项目执行mvn package之后产物大致长这样myapp-1.0.0.jar ├── META-INF/ │ └── MANIFEST.MF # 元信息记录主类、版本、Class-Path 等 ├── com/example/ # 你项目自己的编译产物.class ├── application.yml # src/main/resources 下的资源文件 └── ...注意这里的关键点默认打包只会把你的src/main/java编译出的 class 和src/main/resources里的资源塞进去不会把任何第三方依赖打进去。也就是说如果你的项目用了 Spring、Jackson、MySQL 驱动这些依赖的 class 全都不在 jar 里它们只是躺在本地仓库里被记录在依赖清单上而已。这就是为什么很多人第一次打包后运行会报ClassNotFoundException: org.springframework.boot.SpringApplication之类的错误——jar 里根本没有那个类。理解了这一点你就明白所谓「两种打包方式」的分野在哪了一种是不带依赖的精简包一种是连依赖一起塞进去的可执行包。1.2 两种打包方式的真正区别日常在 IDEA 里给 Maven 项目打 jar我用得最多的两条路子第一种走 Maven 生命周期 IDEA 图形界面或命令行直接 package。它依赖的是项目里配置好的打包插件产物形态完全由pom.xml里的build决定。这是最通用、最基础的方式学习成本最低。第二种配置专门的打包插件spring-boot-maven-plugin/maven-shade-plugin/maven-assembly-plugin来产出可执行 fat jar。这种方式会把依赖一并打进去产物体积大但可以做到java -jar直接跑适合 Spring Boot 项目和需要独立部署的场景。说它们是「两种方式」其实更准确的说法是第一种是「调用打包这件事」第二种是「决定打包这件事的产物长什么样」。两者并不冲突第一种方式里你完全可以用第二种插件。之所以还是一分为二来讲是因为新手最容易混淆的就是这两层概念把它俩捋清楚后面所有操作都是顺水推舟。1.3 场景对照你该选哪条路项目类型运行环境推荐方式产物特征Spring Boot 应用独立服务器、Docker方式二spring-boot 插件 repackage一个可执行 jar内含依赖传统 Java SE 工具类命令行调用方式二shade/assembly 插件fat jarjava -jar可跑被其他项目引用的公共库作为依赖被引入方式一默认 package精简 jar不含依赖部署到已有容器Tomcat的 Web 应用外部容器方式一打成 war 或瘦 jar依赖由容器提供这张表建议存一下。很多人打包出错根源不是命令敲错了而是项目类型和打包方式根本不匹配——拿一个要被别的项目当依赖引用的公共库去打成 fat jar结果依赖冲突满天飞又或者拿一个 Spring Boot 应用按默认方式打包运行时直接找不到主类。1.4 打之前先确认动手的三件事在点任何按钮之前我习惯先做三个检查能省掉一大半回滚重来的时间确认 JDK 版本和项目编译版本一致。IDEA 里Project Structure的 SDK、pom.xml里maven-compiler-plugin的source/target、以及java -version三者必须对得上。用 JDK 17 编译、用 JDK 8 跑报UnsupportedClassVersionError几乎是必然的。确认 pom 里有没有打包插件。翻到build节点看一眼如果没有spring-boot-maven-plugin或类似插件默认打出来的就是精简包别指望java -jar能跑。确认 resources 目录下的配置有没有被过滤掉。application.yml、logback.xml这些如果写在了src/main/java里是打不进去的必须放在src/main/resources。注意IDEA 的图形界面打包和命令行mvn package走的是完全一样的流程都读同一份pom.xml。所以命令行报的错图形界面一样会报别指望换个入口能绕过问题。2. 方式一IDEA 图形界面配合 Maven 生命周期打包这是最省事的一条路也是大多数人日常用的。核心动作就三下打开 Maven 面板、双击clean、双击package。但要把这条路走顺前置配置得先补齐。2.1 前置配置pom.xml 里必须交代清楚的几块先看一份最基础的pom.xml骨架重点看build部分project xmlnshttp://maven.apache.org/POM/4.0.0 modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdmyapp/artifactId version1.0.0/version packagingjar/packaging properties maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies !-- 你的依赖 -- /dependencies build finalNamemyapp/finalName plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration source17/source target17/target encodingUTF-8/encoding /configuration /plugin /plugins /build /project几个点值得单独拎出来说packaging必须是jar。如果你是从老项目复制过来的可能残留着war那打出来的就是 war 包package阶段行为完全不一样。finalName决定产物的名字。不写的话默认是artifactId-version.jar例如myapp-1.0.0.jar。我一般会显式写成myapp因为后面写部署脚本、Dockerfile 的时候固定名字省事得多。project.build.sourceEncoding一定要设成 UTF-8。这个属性不设置Maven 会拿系统默认编码去处理资源文件Windows 上默认 GBK稍不注意打出来的application.yml里中文就是乱码。这是我在项目里被坑过好几次的地方。2.2 在 IDEA 右侧 Maven 面板里一步步点下去配置好了之后操作本身很简单。打开 IDEA 右侧边栏的 Maven 面板没有的话菜单View → Tool Windows → Maven展开你的项目节点找到Lifecycle这一栏里面按顺序列着validate、compile、test、package、verify、install、deploy等若干条目。第一步双击clean。它的作用是删掉target目录把上一次的编译产物清干净。别小看这一步我见过太多「明明改了代码打出来的 jar 还是旧的」的案例十有八九是没 cleantarget目录里残留着上次的 class。第二步双击package。它会按顺序执行compile → test → package也就是先编译主代码再跑单元测试最后打包。如果你项目里的单元测试比较重或者恰好有关系数据库、外部服务的依赖这一步可能会卡很久。第三步去target目录看产物。打包成功的话控制台会输出类似[INFO] Building jar: /path/to/project/target/myapp.jar [INFO] BUILD SUCCESS这时候target目录下应该能看到myapp.jar以及一个classes目录编译后的 class。classes是你项目自己的 class 存放处也是被打进 jar 的原始材料。2.3 命令行等价操作与几个省时间的参数上面的双击操作等价于在项目根目录执行mvn clean package在 IDEA 底部的 Terminal 里直接敲这条命令效果完全一样。命令行有个好处是可以带参数这对日常提速很有用# 跳过测试执行但编译测试代码 mvn clean package -DskipTests # 完全跳过测试的编译和执行速度最快 mvn clean package -Dmaven.test.skiptrue-DskipTests和-Dmaven.test.skiptrue的区别值得说一下。前者只是不运行测试方法但测试代码照样编译如果测试代码本身有编译错误还是会失败后者连测试代码都不编译彻底跳过。日常本地打包图快我用后者但在 CI 上我会保留测试用前者。还有个常用的参数是-o代表离线模式。本地仓库里依赖都全的情况下加上它能让构建不去远程仓库检查更新速度肉眼可见地快尤其是网络不太稳定的时候。缺点是如果本地缺依赖会直接报错而不是去下载。mvn clean package -o -Dmaven.test.skiptrue最后加一个-U的说明它的作用相反是强制去远程仓库检查快照版本更新。团队协作时你的项目依赖了别人正在开发的 SNAPSHOT 包拉不到最新代码时加上它往往能解决。2.4 产物的验证别打完就发走jar 打出来别急着交给运维先自己验一遍。我一般会用三条命令查# 看 jar 里的目录结构 jar tf target/myapp.jar # 看 MANIFEST.MF 内容重点看有没有 Main-Class unzip -p target/myapp.jar META-INF/MANIFEST.MF # 直接尝试运行 java -jar target/myapp.jar第一条命令会列出 jar 内所有条目。如果里面只有META-INF/和com/yourpackage/加上少量配置文件说明这是精简包依赖没进去那java -jar基本跑不起来——除非你之前特意在 MANIFEST 里配了Class-Path并保证依赖都在正确位置。第二条命令查看清单文件正常应该能看到Manifest-Version、Created-By、Build-Jdk这些。如果是个可以独立运行的应用还要有Main-Class: com.example.MyApp。第三条是最直接的验证。报no main manifest attribute说明没配主类报ClassNotFoundException说明依赖没进去或者主类名写错了。这两种错误后面会专门讲怎么排查。实操心得我习惯在项目根目录放一个verify.sh把上面这三条命令串起来每次打包完跑一遍。看起来是小事但能挡住很多「本地能跑、服务器上起不来」的低级问题。3. 方式二用打包插件产出可以直接运行的 jar如果你的目标就是打出一个java -jar能跑的应用包那前面那种默认打包是不够的必须靠插件把依赖一起塞进去。这里按项目类型分三条线来说。3.1 Spring Boot 项目spring-boot-maven-plugin 的标准姿势Spring Boot 项目打可执行 jar 是最常见的需求官方插件spring-boot-maven-plugin就是为这个场景设计的。在pom.xml里加上build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId version3.2.0/version configuration mainClasscom.example.MyApplication/mainClass excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration executions execution goals goalrepackage/goal /goals /execution /executions /plugin /plugins /build这个插件默认绑定到package生命周期阶段执行repackage目标。它的工作方式是先把项目打成普通 jar再把它重新打包成可执行 jar。所以你会看到一个有意思的现象target目录下会有两个 jar一个是myapp.jar原始精简包也可能是.original后缀一个是被 repackage 后覆盖的最终产物。mainClass一般可以不写插件会自动去找带SpringBootApplication注解的类。但如果项目里有多个主类比如还写了命令行工具自动识别就会犯迷糊这时候显式指定最保险。excludes里的lombok是个典型操作。Lombok 只在编译期起作用运行时不需要打进去纯属占空间。这种编译期依赖都建议排除。打包命令和前面一样mvn clean package -Dmaven.test.skiptrue打完之后用jar tf看一眼结构会看到明显的不同BOOT-INF/ ├── classes/ # 你的项目 class ├── lib/ # 所有第三方依赖 jar META-INF/ ├── MANIFEST.MF org/springframework/boot/loader/ # Spring Boot 自己的类加载器BOOT-INF/lib里躺着所有依赖MANIFEST.MF里的Main-Class指向org.springframework.boot.loader.JarLauncher真正的业务主类则记录在Start-Class属性里。这是 Spring Boot 特有的结构JarLauncher负责用自定义类加载器把BOOT-INF/classes和BOOT-INF/lib加载起来本质上是个「jar 中 jar」的启动器。3.2 普通 Java 项目shade 和 assembly 插件怎么选不是 Spring Boot 的老项目想打 fat jar可以用maven-shade-plugin或maven-assembly-plugin。两者能力有重叠但侧重点不同。maven-assembly-plugin的jar-with-dependencies描述符最省事配置简单plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-assembly-plugin/artifactId version3.6.0/version configuration descriptorRefs descriptorRefjar-with-dependencies/descriptorRef /descriptorRefs archive manifest mainClasscom.example.Main/mainClass /manifest /archive /configuration executions execution idmake-assembly/id phasepackage/phase goals goalsingle/goal /goals /execution /executions /plugin它的工作方式是暴力解压所有依赖的 jar把 class 全展开再重新压成一个 jar。弊端也在这不同依赖里如果有同名的文件会直接覆盖且不一定会警告。META-INF/services下的 SPI 配置、spring.factories这类文件被覆盖就是各种「诡异失效」的根源。maven-shade-plugin在这点上升级了它支持ServicesResourceTransformer来合并 SPI 文件还支持类重定位relocation解决包冲突plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId version3.5.1/version executions execution phasepackage/phase goals goalshade/goal /goals configuration transformers transformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer mainClasscom.example.Main/mainClass /transformer transformer implementationorg.apache.maven.plugins.shade.resource.ServicesResourceTransformer/ /transformers /configuration /execution /executions /plugin我的选择习惯是新项目、依赖复杂、有 SPI 机制的用 shade老项目、依赖简单、快速出包assembly 的jar-with-dependencies够用。两者打出来的都是平铺结构的 fat jar用java -jar直接跑。3.3 指定 Main-Class 的几种写法与区别主类配置是打包环节最容易出错的地方不同插件的写法不一样这里统一对照一下插件配置位置写法spring-boot-maven-pluginconfigurationmainClasscom.example.Main/mainClassmaven-assembly-pluginarchivemanifestmainClasscom.example.Main/mainClassmaven-shade-pluginManifestResourceTransformermainClasscom.example.Main/mainClassmaven-jar-pluginarchivemanifestmainClasscom.example.Main/mainClass看起来都是mainClass但位置差之毫厘谬以千里。放进configuration顶层是 Spring Boot 插件专属放进archivemanifest是标准 maven-archiver 的写法绝大多数插件通用。还有一个细节主类名必须是带完整包名的全限定名不能只写Main。写错的后果是运行时Could not find or load main class。我见过的错误写法里这个排第一。3.4 依赖外置让产物瘦下来fat jar 用起来爽但体积是个大问题。一个中等规模的 Spring Boot 应用打包后动辄七八十兆光依赖就占了九成。如果你的部署环境需要频繁更新代码每次上传这么大的包很痛苦。spring-boot-maven-plugin支持把依赖外置。做法是配置layout为ZIP并配合maven-dependency-plugin把依赖复制到外部目录plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-dependency-plugin/artifactId executions execution idcopy-dependencies/id phasepackage/phase goals goalcopy-dependencies/goal /goals configuration outputDirectory${project.build.directory}/lib/outputDirectory /configuration /execution /executions /plugin打完之后target/lib里是所有依赖主 jar 只有几兆。运行时java -cp myapp.jar:lib/* com.example.MainWindows 下分隔符是分号myapp.jar;lib/*。这种瘦包方案在容器化部署里很常见因为 Docker 分层缓存能让依赖层复用代码变一次只推几兆。4. 打包报错排查实录与常见坑打包这事成功的路径只有一条报错的方式五花八门。下面这些是我这些年遇到频率最高的几个整理成速查表放在前面后面逐条展开。报错信息大概率原因处理方向no main manifest attributeMANIFEST 里没配 Main-Class补配置或确认插件执行了ClassNotFoundException依赖没打进去改用 fat jar 插件UnsupportedClassVersionError编译和运行 JDK 版本不一致统一 JDK 版本Invalid or corrupt jarfilejar 传输损坏或用了 oss 后缀重传核对 MD5打包时中文乱码资源编码未指定 UTF-8配 sourceEncodingBUILD FAILURE 找不到依赖私服/镜像仓库配置问题检查 settings.xml4.1 no main manifest attribute 到底怎么回事这个报错几乎是新手第一坑。原因就一个jar 的 MANIFEST.MF 里没有Main-Class这一行。默认的maven-jar-plugin不会自动写主类除非你显式配置plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-jar-plugin/artifactId version3.4.1/version configuration archive manifest mainClasscom.example.Main/mainClass addClasspathtrue/addClasspath classpathPrefixlib//classpathPrefix /manifest /archive /configuration /pluginaddClasspath和classpathPrefix这两个参数配合起来会在 MANIFEST 里生成一行Class-Path: lib/xxx.jar lib/yyy.jar告诉 JVM 去lib/目录找依赖。这是一种「主 jar 平级 lib 目录」的经典布局和前面讲的依赖外置思路类似只是实现路径不同。排查这条错误的具体步骤先用unzip -p target/myapp.jar META-INF/MANIFEST.MF看内容确认有没有Main-Class如果没有看你的pom.xml里有没有配。注意 IDEA 里有时会缓存 MANIFEST 配置改了 pom 之后建议mvn clean再 package不然改动可能不生效。4.2 ClassNotFoundException 与 NoClassDefFoundError 的排查这两个错误长得很像但含义不同。ClassNotFoundException是主动加载某个类时找不到比如Class.forName()或main方法入口类缺失NoClassDefFoundError是编译时存在、运行时找不到通常发生在某个类的静态初始化之后。在打包场景下这两个错误的共同根源基本都是依赖缺失。排查方法# 看 jar 里到底有没有那些依赖的 class jar tf target/myapp.jar | grep org/springframework/boot/SpringApplication如果 grep 不出来那依赖确实没进去。这时候就要回头看你的打包插件配置。常见疏漏有三个插件没绑定到package阶段executions没写、scopeprovided/scope用多了provided 的依赖不会进包、excludes把不该排除的排除了。特别注意provided这个 scope。它的语义是「编译和测试时需要打包和运行时由容器提供」。本地跑得好好的一打成 fat jar 就少类八成是某个依赖被标了 provided。开发阶段想省事直接用它部署时就要小心。4.3 编码、时区与配置文件被漏打这几类问题不会报错但会在运行时以奇怪的方式暴露出来排查起来最费劲。中文乱码。前面提过源头是资源编码未指定。除了设project.build.sourceEncoding还要注意 IDEA 里Settings → Editor → File Encodings的全局编码三个地方Global Encoding、Project Encoding、Default encoding for properties files全部设 UTF-8。properties 文件历史上默认按 ISO-8859-1 处理虽然现代 Maven 能覆盖但设成 UTF-8 更保险。时区问题。容器里通常默认 UTC容器外可能是东八区写日志时间对不上会影响排查。打运行时包的时候我习惯加一个 JVM 参数启动参数-Duser.timezoneAsia/Shanghai或者在 Dockerfile 里设TZ环境变量。配置文件漏打。application.yml放在src/main/java下是打不进去的。Maven 只认识src/main/resources。这个坑我中过一次本地 IDEA 里跑得好好的IDE 把 java 目录下的资源也算进 classpath 了打包后配置全丢找了好久才发现文件位置不对。4.4 仓库配置引发的构建失败热词里频繁出现「maven 配置阿里云仓库」说明这是很多人踩过的地方。默认的中央仓库在国内访问速度感人构建经常超时。在~/.m2/settings.xml里的mirrors节点加一段mirror idaliyunmaven/id mirrorOfcentral/mirrorOf namealiyun maven/name urlhttps://maven.aliyun.com/repository/public/url /mirrormirrorOfcentral/mirrorOf表示接管中央仓库。如果公司还有私服写成mirrorOf*,!my-private/mirrorOf这种形式把私服 id 排除出去。配置完之后如果某个依赖还是拉不下来先清掉本地仓库对应目录再重试mvn clean package -U-U强制更新 SNAPSHOT对发布版一般无效但对 SNAPSHOT 依赖有效。另外注意本地仓库里经常会有xxx.jar.lastUpdated这种文件它们是上次下载失败的标记网络恢复后如果不带-UMaven 可能直接跳过重试。稳妥做法是手动删掉这些.lastUpdated文件find ~/.m2/repository -name *.lastUpdated -delete这条命令在 Linux/macOS 上直接可用Windows 下用 PowerShell 或者干脆手动删。清理完再打包成功率会高不少。4.5 外部 jar 包引入的正确姿势老项目里经常有「手动引入本地 jar」的需求。热词里提到springbootmaven 导入外部 jar 包这个场景确实麻烦。不推荐的方式是用systemscopedependency groupIdcom.vendor/groupId artifactIdlegacy-sdk/artifactId version1.0/version scopesystem/scope systemPath${project.basedir}/lib/legacy-sdk.jar/systemPath /dependency它能编译通过但systemscope 在较新版本的 Maven 里已经被标记为不推荐而且这个依赖不会被打进 fat jar部署时还是得手动带上。稳妥的做法有两条。一是把 jar 手动安装到本地仓库mvn install:install-file -Dfilelib/legacy-sdk.jar \ -DgroupIdcom.vendor \ -DartifactIdlegacy-sdk \ -Dversion1.0 \ -Dpackagingjar安装之后再按正常依赖引用本地能编译但团队其他人拉代码时仍需各自执行一遍。更彻底的方式是搭建内网私服把 jar 传上去所有成员统一从私服拉。公司项目里我一般推后者个人项目前者够用。5. 一些能提效的实操技巧前面讲的都是主线流程这一节聊点散装的、但日常很好用的东西。5.1 多环境 profile 与打包的配合项目通常有 dev、test、prod 三套配置。经典做法是按环境拆application-dev.yml、application-test.yml、application-prod.yml然后通过spring.profiles.active激活。打包时用 Maven 的 profile 把对应环境的配置复制进产物profiles profile idprod/id properties envprod/env /properties build resources resource directorysrc/main/resources/directory filteringtrue/filtering includes includeapplication.yml/include includeapplication-${env}.yml/include /includes /resource /resources /build /profile /profiles打包命令带上 profilemvn clean package -Pprod -Dmaven.test.skiptruefilteringtrue/filtering会开启占位符替换application.yml里写${env}的会被替换成 profile 里的值。这里有个注意点开启 filtering 后所有xxx和${xxx}形式的字符串都会被尝试替换如果你的配置文件里有Value注解或者路径中带${}的表达式可能被误伤。规避方法是在文件顶部用#set($x...)#[[...]]#之类的转义语法或者干脆只对指定文件开启 filtering。5.2 反编译验证确认代码真的打进去了打包这件事最怕的不是报错是「看起来成功了但内容不对」。有一种办法可以直接验证 jar 里的 class 是不是最新代码反编译。javap是 JDK 自带的最简单javap -c -p -classpath target/myapp.jar com.example.Main-c输出字节码-p显示私有成员。不加-c就只看类签名。想看得更舒服可以用图形化反编译工具直接把 jar 拖进去就能浏览源码结构的工具不少社区里比较常见。什么时候用得上这招两个场景一是怀疑打进去的是旧代码反编译看看方法体里有没有你新加的逻辑二是接手别人维护的老项目jar 是唯一产物没有源码想弄清楚它到底做了什么。这算是打包的一个副产品技能但确实实用。5.3 用 Docker 打包时容易忽略的细节打包和 Docker 结合的场景现在太常见了热词里也有idea 打包 docker 镜像。这里容易出问题的地方主要是镜像构建阶段的maven缓存和产物拷贝。一个我常用的Dockerfile骨架FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -Dmaven.test.skiptrue FROM eclipse-temurin:17-jre WORKDIR /app COPY --frombuilder /build/target/myapp.jar app.jar ENV TZAsia/Shanghai ENTRYPOINT [java, -jar, app.jar]关键点在于COPY pom.xml和mvn dependency:go-offline这一步单独做。Docker 的分层机制会让这一层被缓存只要 pom 不变后续构建就不用重新下载依赖构建时间能从几分钟降到十几秒。这是实打实的效率提升值得每个做容器化的项目都用上。另外FROM maven:xxx AS builder这种多阶段构建最终镜像里只保留jre基础镜像和 jar不会有 Maven、源码这些镜像体积小很多。生产环境强烈建议这么干。提醒一下时区。基础镜像默认几乎都是 UTC日志时间会差八个钟头。前面ENV TZAsia/Shanghai那行别省省了之后排查问题会怀疑人生。5.4 版本号管理与产物归档最后说个容易被忽略但很重要的实践别让target目录成为唯一产物来源。本地打出来的 jar 分散在各个项目的 target 目录时间一长就找不到「上周发给运维那个版本是哪次构建的」。我的做法是在构建脚本里带上时间戳和 git 短哈希VERSION$(git rev-parse --short HEAD) TIMESTAMP$(date %Y%m%d%H%M) mvn clean package -Dmaven.test.skiptrue cp target/myapp.jar dist/myapp-${TIMESTAMP}-${VERSION}.jar这样每次构建都留档出问题能快速回滚到已知可用的版本。团队里如果要正式一点就是上 CI 工具让每次合入自动构建、自动上传到制品库人工只负责触发部署。这一步看起来和「打包」关系不大但你踩过几次「旧包覆盖新包、版本对不上」的坑之后就会知道留档这事有多值。写到这里基本把 IDEA 配合 Maven 打 jar 的两种主线和周边都过了一遍。我个人的体会是打包这件事真正的门槛不在操作而在「想清楚产物要长什么样」——是给别的项目当依赖还是自己独立运行是本地调试用还是上生产。答案不同选型就不同。把这一点想明白剩下的配置和命令都是照着填的事。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻