ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

IDEA打包jar后找不到主类?排查清单与配置指南

IDEA打包jar后找不到主类?排查清单与配置指南 简介这份PDF资料面向使用IntelliJ IDEA进行Java开发的程序员尤其是打包可执行jar时遇到“找不到或无法加载主类 main”报错的开发者。内容围绕该常见问题的成因与解决思路展开涵盖Maven项目中通过maven-jar-plugin配置mainClass与addClasspath、IDEA Build Artifacts方式生成可运行jar以及Windows下CLASSPATH环境变量排查等关键环节适合具备一定Java与Maven基础、需要快速定位打包故障的读者参考。资源包共1个PDF文件约258KB篇幅精炼便于随时查阅对照。目前已有20875人学习下载说明该问题在实际开发中较为普遍。读者可从中获得主类配置写法、插件参数含义、环境变量调整方向与排错思路减少反复试错成本提升jar打包与运行的成功率。1. IDEA 打包 jar 后报「找不到或无法加载主类 main」先别急着改代码你刚在 IDEA 里点完 Build Artifacts或者跑完mvn package终端里敲下java -jar xxx.jar结果迎面一句错误: 找不到或无法加载主类 xxx或者no main manifest attribute。这个场景太常见了常见到几乎每个 Java 后端都至少踩过一次。它跟你的业务代码基本没关系问题几乎全部出在「打包产物」和「启动方式」这两件事上要么 MANIFEST.MF 里没写对Main-Class要么依赖没进去导致主类加载时抛NoClassDefFoundError被吞掉要么你用了 Spring Boot 的spring-boot-maven-plugin却用普通java -jar去跑一个非可执行 jar。这篇就把 IDEA 打包 jar 后主类找不到的排查链路、参数配置和几个高频翻车点讲透适合正在用 IDEA 社区版或旗舰版、用 Maven/Gradle 构建、准备把项目打成可执行 jar 的开发者。看完你能自己定位是清单问题、依赖问题还是启动命令问题而不是反复 Clean 重来。2. 先搞清「找不到主类」到底是谁在报错JVM 加载链路拆解2.1 从 java -jar 到 main 方法中间隔了四道关很多人一看到「找不到主类」就以为是代码里main方法写错了其实 JVM 根本没走到你的main方法。java -jar app.jar的执行链路是这样的JVM 先读 jar 包META-INF/MANIFEST.MF找Main-Class属性找到后按这个全限定类名去 jar 的 classpath 里加载类加载成功后反射调用它的public static void main(String[])。任何一环断了报错信息都不一样。没有Main-Class属性报no main manifest attribute, in xxx.jar有Main-Class但类不在 jar 里报Could not find or load main class xxx类在但依赖的类不在报NoClassDefFoundError或ClassNotFoundException有时被包装成主类找不到类在、依赖也在但main方法签名不对报Main method not found in class xxx所以第一步不是改代码是先把报错原文完整读一遍。IDEA 的 Run 窗口和终端报错经常被截断建议直接在终端跑把完整堆栈贴出来。我一般会先执行unzip -l app.jar | head -50看 jar 里到底有什么再unzip -p app.jar META-INF/MANIFEST.MF看清单内容两分钟就能定位到是哪一环。2.2 MANIFEST.MF 里到底该写什么一个能跑的最小可执行 jarMANIFEST.MF 至少要有这几行Manifest-Version: 1.0 Main-Class: com.example.demo.DemoApplication Class-Path: lib/commons-lang3-3.12.0.jar lib/guava-31.1-jre.jar注意几个细节Main-Class后面跟的是全限定类名不带.class后缀文件最后一行如果是Class-Path这种长属性必须保证末尾有换行否则最后一个字符会被吞掉这是血泪经验Class-Path里的路径是相对于 jar 所在目录的不是相对于 jar 内部。如果你把依赖打进了 jar 内部fat jar就不需要Class-Path但需要特殊的类加载器去读这就是 Spring Boot 那套BOOT-INF结构的由来。2.3 普通 jar、fat jar、Spring Boot 可执行 jar 的区别这三种 jar 的启动方式完全不同混用就是主类找不到的头号原因。类型结构启动方式主类位置普通 jar只有自己的 classjava -cp手动指定依赖MANIFEST 或命令行fat jarshade自己的 class 依赖解压合并java -jarMANIFEST 的 Main-ClassSpring Boot jarBOOT-INF/classesBOOT-INF/libjava -jarMANIFEST 的 Start-ClassSpring Boot 的 jar 里Main-Class是org.springframework.boot.loader.JarLauncher真正的业务主类写在Start-Class里。如果你手动改了 MANIFEST 把Main-Class指向自己的类JarLauncher 那套类加载机制就废了依赖全找不到报错看起来就像主类找不到。所以用 Spring Boot 就老老实实用它的插件别手改清单。3. IDEA 里打包可执行 jar 的三条路Artifacts、Maven、Gradle 怎么选3.1 用 IDEA Artifacts 打 fat jar 的完整步骤IDEA 自带的 Artifacts 适合不想动构建脚本、快速出包的场景。步骤是File → Project Structure → Artifacts → 加号 → JAR → From modules with dependencies。在弹出的窗口里Main Class 选你的启动类JAR files from libraries 选extract to the target JAR这是打 fat jar 的关键选 copy 的话依赖会散落在外面。配置完回到 Build → Build Artifacts → Build产物默认在out/artifacts/项目名_jar/下。这里有个高频坑如果你之前构建过IDEA 可能复用旧的清单建议先 Clean 再 Build。构建完用unzip -p验证清单unzip -p out/artifacts/demo_jar/demo.jar META-INF/MANIFEST.MF正常应该能看到Main-Class: com.example.demo.DemoApplication。如果这行是空的或者指向了别的类说明 Main Class 没选对回 Project Structure 重选。Artifacts 方式对多模块项目不太友好模块依赖容易漏所以中大型项目我更推荐 Maven。3.2 Maven 打可执行 jarmaven-jar-plugin 与 shade 插件普通 Maven 项目默认打的 jar 不含依赖也不写 Main-Class直接java -jar必然报主类找不到。要打成可执行 jar常见做法是配maven-jar-plugin写清单再用maven-dependency-plugin把依赖拷到 lib 目录plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-jar-plugin/artifactId version3.3.0/version configuration archive manifest !-- 指定启动类打包时写入 MANIFEST.MF -- mainClasscom.example.demo.DemoApplication/mainClass !-- 把依赖 jar 的相对路径写进 Class-Path -- addClasspathtrue/addClasspath classpathPrefixlib//classpathPrefix /manifest /archive /configuration /pluginmainClass填全限定类名classpathPrefix决定 Class-Path 里依赖的前缀配合 dependency-plugin 的copy-dependencies输出到target/lib这样java -jar target/demo.jar才能找到依赖。如果依赖没拷过去主类能加载但一初始化就抛NoClassDefFoundError报错信息里往往带着主类名很容易误判。想打成单文件 fat jar就用maven-shade-pluginplugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId version3.5.0/version executions execution phasepackage/phase goalsgoalshade/goal/goals configuration transformers transformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer !-- shade 插件负责写 Main-Class -- mainClasscom.example.demo.DemoApplication/mainClass /transformer /transformers /configuration /execution /executions /pluginshade 会把所有依赖解压合并进一个 jar注意它和 Spring Boot 插件不能同时用否则清单会被覆盖主类指向谁就说不准了。3.3 Spring Boot 项目用 spring-boot-maven-plugin 而不是手写清单Spring Boot 项目最省事的做法是直接用官方插件它会生成BOOT-INF结构并写好Main-Class和Start-Classplugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration !-- 指定要执行的启动类多模块时尤其重要 -- mainClasscom.example.demo.DemoApplication/mainClass /configuration executions execution goalsgoalrepackage/goal/goals /execution /executions /pluginrepackage这个 goal 是关键它把普通 jar 重新打成可执行 jar。很多人只加了插件没配 executions结果打出来还是普通 jarjava -jar就报 no main manifest attribute。多模块项目里插件要放在真正含启动类的那个模块放在父 pom 的 pluginManagement 里只声明不生效。验证方式是unzip -p target/demo.jar META-INF/MANIFEST.MF看到Start-Class是你的类、Main-Class是JarLauncher就对了。4. 主类找不到的排查清单从清单、依赖到启动命令逐层验证4.1 三步定位法看清单、看类、看依赖遇到报错别乱试按这个顺序走。第一步看清单unzip -p app.jar META-INF/MANIFEST.MF确认Main-Class存在且类名拼写正确注意包名大小写。第二步看类在不在unzip -l app.jar | grep DemoApplication普通 jar 应该能看到com/example/demo/DemoApplication.classSpring Boot jar 应该在BOOT-INF/classes/下。第三步看依赖如果是NoClassDefFoundError报错里会带缺失的类名去BOOT-INF/lib或lib/里找对应的 jar。# 一条命令同时看清单和主类是否存在 unzip -p app.jar META-INF/MANIFEST.MF \ unzip -l app.jar | grep -i DemoApplication.class如果清单里 Main-Class 指向的类在 jar 里找不到基本就是打包时主类选错或者多模块里打错了模块。如果类在但启动报依赖缺失就是 fat jar 没打全检查插件配置里依赖是否被排除。4.2 启动命令写错也会伪装成主类问题java -jar和java -cp是两套逻辑。用java -cp app.jar com.example.demo.DemoApplication时JVM 忽略 MANIFEST 的 Main-Class直接按你给的类名找这时如果类名写错或依赖不在 classpath报错也是找不到主类。反过来用java -jar时命令行再跟类名是无效的JVM 只认清单。还有一种情况是 jar 路径写错java -jar找不到文件时报的是Unable to access jarfile不是主类问题别混。Spring Boot 项目如果用了spring-boot-maven-plugin启动必须java -jar不能java -cp因为BOOT-INF结构需要 JarLauncher 来解析。用java -cp跑 Spring Boot jar 必然报主类找不到这是新手最容易翻的车之一。4.3 用 -verbose:class 和 -Xdiag 看 JVM 到底加载了什么报错信息不够时加 JVM 参数看细节# 打印类加载过程确认主类是否被尝试加载 java -verbose:class -jar app.jar 21 | grep -i DemoApplication # 打印 jar 清单解析和类路径诊断 java -Xdiag -jar app.jar 21 | head -30-verbose:class能看到 JVM 有没有去加载你的主类如果压根没出现说明清单里的类名和实际不符如果出现了但后面跟ClassNotFoundException就是类路径问题。-Xdiag会输出清单解析细节对排查Class-Path写错特别有用。这两个参数在本地排查足够生产环境慎用输出量大。5. 避坑与常见问题主类找不到的五个高频翻车现场5.1 现象no main manifest attribute原因插件没写 Main-Class解决补配置这是最常见的一类。Maven 默认的maven-jar-plugin不写 Main-ClassSpring Boot 插件没配repackage也不写。解决就是按第 3 章的配置补上mainClass或repackagegoal。验证方式固定为unzip -p看清单别靠猜。注意 IDEA Artifacts 方式如果 Main Class 下拉框没选清单里同样没有这行。5.2 现象Could not find or load main class类名看着没错原因包名大小写或路径不符解决核对全限定名Java 包名大小写敏感com.example.Demo和com.example.demo是两个类。IDEA 里选 Main Class 时如果手输很容易把包名写错。还有一种情况是类在默认包没有 package 声明这时 Main-Class 只能写类名不能带包名但默认包在可执行 jar 里支持很差建议永远给启动类加包声明。核对方法就是unzip -l看实际路径逐字符比对。5.3 现象NoClassDefFoundError 被当成主类找不到原因fat jar 没打全依赖解决检查插件依赖范围报错堆栈里如果出现Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication说明主类加载了但依赖缺失。常见原因是依赖 scope 是provided或test打包时被排除或者 shade 插件配了minimizeJar把没显式引用的类裁掉了。解决是把运行必需的依赖改成compilescope或关掉 minimizeJar。Spring Boot 项目检查BOOT-INF/lib下 jar 数量是否正常。5.4 现象IDEA 里能跑打包后不行原因IDEA 用了模块 classpathjar 没有解决以 jar 为准验证IDEA 运行配置默认用模块的 classpath依赖是 IDE 拼出来的跟 jar 里的清单和依赖布局无关。所以「IDEA 能跑」不能证明 jar 没问题。正确做法是打包后一定在终端用java -jar跑一遍别只在 IDEA 里点运行。我一般会在 CI 里加一步java -jar target/app.jar --help之类的冒烟测试提前暴露清单问题。5.5 现象改了主类但报错还是旧的原因增量构建复用了旧清单解决clean 后重新打包IDEA 和 Maven 都有增量构建改了启动类或插件配置后旧的 MANIFEST.MF 可能被复用。表现是明明配置改了unzip -p看到的还是旧类名。解决就是mvn clean package或 IDEA 里 Build → Rebuild Project确保 target 或 out 目录被清空。这个坑不复杂但很耗时间养成改配置就 clean 的习惯。6. 进阶用 maven-assembly-plugin 自定义布局与启动脚本当你需要把 jar、依赖、配置文件、启动脚本打成一个 zip 分发时maven-assembly-plugin比 shade 更灵活。它能自定义目录结构把config/和lib/分开再配一个start.sh指定 classpath这样主类问题从「清单写死」变成「脚本显式指定」排查更直观。plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-assembly-plugin/artifactId version3.6.0/version configuration descriptors !-- 自定义 assembly 描述文件定义目录布局 -- descriptorsrc/assembly/dist.xml/descriptor /descriptors /configuration executions execution idmake-dist/id phasepackage/phase goalsgoalsingle/goal/goals /execution /executions /plugin对应的dist.xml里用dependencySet把依赖输出到lib/用fileSet把配置输出到config/。启动脚本里写java -cp lib/*:config com.example.demo.DemoApplication主类由脚本显式给出不依赖清单出问题时看脚本和目录结构即可。这种方式在需要给运维交付、又不想用 Spring Boot 嵌套 jar 的场景很实用。验证方法我习惯固定成三步unzip -l看布局、java -cp本地跑通、再在目标机器上跑一遍确认路径分隔符Windows 用;Linux 用:。跨平台分发时 classpath 分隔符写错也会报主类找不到这个坑在混合环境里很常见。最后说个我自己的习惯每次打包完先unzip -p看清单再java -jar跑一遍两步都过了才提交构建产物。主类找不到这个问题九成不是代码问题是打包配置和启动方式没对齐把清单和依赖这两件事盯死基本不会再翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表