拓冰建站拓冰建站
首页 / 资讯中心 / 正文

IDEA打jar包全攻略:从普通Java到Spring Boot及外部依赖处理

干Java这行的几乎没人能绕开“打jar包”这三个字。不管是把自己写的工具类发给同事还是把一个Spring Boot服务部署到Windows服务器上最后一步基本都得落到“怎么打出一个能跑的jar包”上。但我发现一个很有意思的现象同样问“Intellij怎么打jar包”完全可能是三种不同的人在问——用普通Java项目的人想打一个能双击运行的小工具用Maven构建的Spring Boot开发者想把整个服务打包部署还有一类人手里的项目是接了京东SDK、阿里云SDK这类外部JAR的光把代码打完还不够外部依赖也得跟着进去。这三种场景在IDEA里的操作路径完全不同网上很多教程只讲了其中一种读者照着做就踩坑。这篇文章我就一次性把IDEA里打jar包的几种主流姿势讲清楚包括图形界面操作、Maven命令、外部本地JAR的三种处理路线以及我自己在项目里遇到过的各种报错和排查思路。无论是刚接触IDEA的Java学习者还是准备把Spring Boot服务扔到Windows服务器上运行的开发应该都能从里面找到能直接抄作业的内容。1. 先把概念理清楚你要打的到底是什么“包”1.1 JAR、可执行JAR、Fat JAR、Spring Boot JAR的区别很多新手对“jar包”的理解其实停留在“它是一个压缩文件”的层面这没错但不够。JARJava ARchive本质上就是一个ZIP格式的压缩包里面装的是编译后的.class字节码文件、配置文件、资源文件以及一个描述自身信息的META-INF/MANIFEST.MF清单文件。普通JAR包就好比一个零件仓库里面的类可以被别的项目引用但它自己不知道怎么启动。可执行JAR和普通JAR最大的区别就是在MANIFEST.MF里多了一行Main-Class配置相当于在仓库门口挂了个“从这里进去找门卫”的牌子。JVM执行java -jar xxx.jar的时候会先去读这个Main-Class然后找到对应的main方法启动程序。Fat JAR也叫uber JAR则是把项目依赖的所有第三方JAR全部解压后重新合并到一个大JAR里这样整个程序就只有一个独立的文件拷贝到任何装了JDK/JRE的机器上都能直接跑。Spring Boot的可执行JAR外观上也是单个文件但内部结构不太一样它把依赖放在BOOT-INF/lib目录下类和资源放在BOOT-INF/classes下用自定义类加载器去加载所以你既不能把Spring Boot的JAR当成普通JAR塞到别的项目的classpath里也不能简单粗暴地把它的内容解压后合并到别的包里。了解这些之后你就能明白一个关键结论普通Java项目用IDEA的Artifact功能打包没问题但Spring Boot项目最好老老实实用Maven/Gradle插件打因为IDEA的Artifact打包根本不认识Spring Boot的启动逻辑。1.2 为什么IDEA不能对“所有项目”一键打包IDEA界面上的确有个Build → Build Artifacts菜单很多新手点了之后发现要么没反应要么打出来的包运行报错。原因是IDEA自己并不是一个构建工具它只是一个集成开发环境。对于普通的纯Java项目IDEA可以通过Artifact的配置把编译产物组织成JAR但对于复杂项目真正负责编译、依赖解析、打包的是Maven或GradleIDEA只是把它们的界面和操作折叠成了几个按钮。所以你在问“Intellij怎么打jar包”之前先看一眼项目根目录有没有pom.xml或者build.gradle文件。有的话你就已经在用Maven/Gradle的世界里了打包方式应该围绕这两个工具来只有完全没有构建脚本的纯Java项目才值得去用IDEA的Artifact图形界面。2. 普通Java项目用IDEA图形界面打出可执行JAR包2.1 前置准备先把Project SDK和编译输出确认好打开IDEA后第一件事不是直接去配Artifact而是确认项目的SDK。按CtrlShiftAltSWindows或者Cmd;Mac打开Project Structure在Project选项卡里确认Project SDK选的是你本机装好的JDKProject language level也要和代码里用的语法版本匹配。如果你代码里用了Java 11的var关键字但SDK选的是Java 8编译阶段就会报错。另外建议先执行一次Build → Build Project快捷键CtrlF9确保当前代码能完整编译通过。很多打包失败其实根本不是打包配置的问题而是代码本身编译不过IDEA会直接中断后续流程。这个习惯能帮你把问题定位范围缩小很多。2.2 新建Artifact完整菜单操作与参数解释确认SDK没问题之后开始配置Artifact。流程如下File → Project Structure左侧选择Artifacts。点击中间的号选择JAR → From modules with dependencies。弹出的窗口里Module选择你的主模块Main Class点击右侧的文件夹图标选择包含main方法的类。下面有个JAR files from libraries的选项默认是extract to the target JAR意思是把第三方依赖解压后和你的代码合并进同一个JAR这也就是前面说的Fat JAR方案。如果你希望依赖保持原样、以lib目录形式放在最终JAR旁边就选copy to the output directory and link via manifest。在Directory for META-INF/MANIFEST.MF这一栏IDEA会默认指向src/main/java这是很多新手踩坑的高发位置。最好手动改成src/main/resources否则生成出来的MANIFEST.MF会出现在源码目录里有时还会污染Git提交看着相当难受。设置完成之后界面下方会生成一个Available Elements列表你可以在这里勾选哪些模块内容进入JAR、哪些排除。如果项目里有测试代码记得把测试相关的模块从列表里移除否则打出来的包会带着一堆测试类。2.3 触发构建Build → Build Artifacts配置完成后回到主界面执行Build → Build Artifacts选择你刚创建的Artifact名称再选Build或RebuildRebuild会清空缓存重新构建通常更干净。构建完成后在Project Structure里设置的Output directory默认是项目的out/artifacts/项目名_jar目录下就能找到生成的JAR文件。如果最终JAR大小只有几个KB说明依赖没有一起打进去多半是JAR files from libraries那边选错了选项如果你选了extract to the target JAR最终产物会比较大但单独这一个文件就能跑。验证方式很简单打开命令行cd到输出目录执行java -jar 你的包名.jar。如果程序正常启动、功能正常说明这个JAR没问题可以发给别人用了。2.4 图形界面打包的坑签名文件冲突和重复类IDEA自带的Artifact Fat JAR方案在实际工程里用得多了就会发现两个问题。第一个是签名文件冲突。很多第三方JAR比如某些官方SDK自带的META-INF目录里有.SF、.DSA、.RSA签名文件IDEA把所有依赖解压合并进一个JAR时这些签名文件会互相覆盖。最终表现就是运行时报SecurityException: Invalid signature file digest for Manifest main attributes。解决办法是构建完后手动删除JAR里META-INF下多余的签名文件或者在Artifact配置里把对应依赖排除掉、只保留一个。第二个是重复类覆盖。如果两个依赖JAR里存在同名类IDEA合并时无法感知优先级谁先解压谁就生效结果就是运行期出现诡异的行为或直接NoSuchMethodError。IDEA的图形界面没有提供精细化的依赖冲突管理能力遇到这种场景最靠谱的还是切换到Maven的shade插件去控制。3. Spring Boot / Maven项目打包别再用Artifact了3.1 Maven生命周期clean、package、install之间怎么选只要项目里有pom.xml先忘掉Build Artifacts这个选项直接走Maven的生命周期。Maven打包有明确的生命周期阶段常用的是clean、package、install三个。mvn clean负责删除target目录下之前的所有构建产物避免旧文件残留影响结果mvn package负责把项目打成JAR或WAR包输出到target目录mvn install在package基础上把构建产物安装到本地Maven仓库默认在用户目录的.m2/repository下这样其他本地项目就能以依赖方式引用你打的包。实际发布Spring Boot服务时我推荐用mvn clean package保证从零开始构建。如果你只想打一个包然后部署到服务器package就够了不需要install。3.2 在IDEA里执行Maven打包右侧面板还是TerminalIDEA提供了两种执行Maven命令的方式。第一种是右侧的Maven工具窗口展开Lifecycle目录先双击clean再双击package。这种方式对新手最友好因为执行结果会以清晰的面板展示点击某个报错可以直接跳到对应代码。第二种方式是在IDEA底部自带的Terminal里直接敲mvn clean package效果完全一样。我个人更推荐第二种因为习惯了命令行之后你在本地、在服务器、在CI环境里的操作是一致的不用维护两套心智模型。另外在Terminal里执行还能顺手加参数比如跳过测试的-DskipTests比在Maven面板里点来点去快得多。打包完成后target目录下会出现两个JAR文件一个以-plain结尾或者叫-original取决于插件配置一个不带后缀。带后缀的那个才是Spring Boot的可执行JAR注意区分别发错了。3.3 打包前必改的pom.xml配置Spring Boot项目如果直接在pom.xml里什么都不配就执行mvn package大概率也能打出一个可执行JAR因为spring-boot-starter-parent已经帮你配好了spring-boot-maven-plugin。但有几个细节建议手动确认一下。第一是finalName。在build节点下加一个finalName可以自定义打出来的JAR文件名。比如build finalNameuser-center/finalName plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration mainClasscom.example.UserCenterApplication/mainClass /configuration /plugin /plugins /buildmainClass建议显式指定。如果项目里存在多个带main方法的类比如一个启动类、一个数据初始化工具类插件自动检测时有可能选错最后打出来的包启动后报错或行为不符合预期。第二是跳过测试。默认执行mvn package时会跑一遍所有单元测试如果测试有环境依赖比如连数据库、调外部接口打包过程可能卡几分钟甚至直接失败。不想跑测试就用mvn clean package -DskipTests-DskipTests是编译测试类但不执行如果想连测试代码编译都跳过用-Dmaven.test.skiptrue。3.4 部署前最后验证本地java -jar跑起来打包完成后不要急着扔到服务器上先在本机验证一次。命令行执行java -jar target/user-center.jar看到类似Started UserCenterApplication in xx seconds的日志说明包没问题。这个验证动作虽然简单但能提前暴露90%的“包打坏了”问题比如主类选错、依赖缺失、资源文件没打进去等。在Windows服务器上运行时建议把启动和停止写成脚本。start.bat内容大概是这样echo off start user-center javaw -jar user-center.jar --server.port8080用javaw而不是java可以避免启动后命令行窗口一直挂着要停止服务的话在任务管理器里按照进程名结束对应Java进程或者用netstat -ano找到占用8080端口的PID再taskkill /PID pid /F。这里有个容易被忽略的点服务器上必须已经安装JDK并配好JAVA_HOME环境变量单纯装个JRE在某些情况下跑Spring Boot的可执行JAR容易出问题最好统一用JDK环境。4. 引入本地JAR包后怎么打外部依赖的三条处理路线4.1 场景说明官方SDK不在Maven中央仓库怎么办实际项目中经常遇到这种问题采购了某个平台的服务对方给了一个JDK的SDK压缩包里面是一堆JAR文件但Maven中央仓库里根本找不到对应的依赖坐标。京东的某些对接SDK、老牌硬件厂商的串口通信SDK都有这种情况。这时候你的项目代码能写出来是因为IDEA在编译期能找到这些JAR但最终打出来的JAR包能否带着这些依赖一起跑取决于你用了哪种导入方式。4.2 路线一把本地JAR安装到Maven本地仓库推荐最干净的做法是把本地JAR当作一个标准Maven依赖手动安装到本地仓库mvn install:install-file \ -Dfilejd-open-sdk-1.0.jar \ -DgroupIdcom.jd \ -DartifactIdjd-open-sdk \ -Dversion1.0 \ -Dpackagingjar执行完这条命令之后本地Maven仓库的com/jd/jd-open-sdk/1.0目录下就会出现对应的JAR文件。然后你在项目的pom.xml里像引用普通依赖一样写dependency groupIdcom.jd/groupId artifactIdjd-open-sdk/artifactId version1.0/version /dependency这样后面的一切行为编译、打包、传递依赖都跟在中央仓库拉下来的依赖没有任何区别Spring Boot插件会把这个依赖打进去。这是最稳妥、最不容易出幺蛾子的方案。4.3 路线二把JAR放在项目lib目录用system scope有些朋友拿到SDK后习惯直接丢到项目的lib目录下然后在IDEA里右键Add as Library代码编译当然没问题但Maven打包时根本不认识这个依赖最终JAR运行时会报NoClassDefFoundError。要让Maven在打包时把这个本地JAR也带进去可以在pom.xml里这样配dependency groupIdcom.jd/groupId artifactIdjd-open-sdk/artifactId version1.0/version scopesystem/scope systemPath${project.basedir}/lib/jd-open-sdk-1.0.jar/systemPath /dependencysystemscope的意思是“依赖在本地不走仓库”配合systemPath指向具体文件Maven编译时会用到它。再配合spring-boot-maven-plugin的includeSystemScope配置就能把它打进最终的可执行JARplugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration includeSystemScopetrue/includeSystemScope /configuration /plugin但这条路线有两个隐患一是systemscope的依赖在发布到私有Maven仓库时不会被跟随传递团队里其他人拿到项目后如果本地没有lib目录下的JAR编译直接失败二是如果以后要换到Nexus私服或者中央仓库还得改回正常依赖。它适合临时、单机、个人项目团队合作时强烈不推荐。4.4 路线三自己搭个私有Maven仓库把SDK传上去如果团队不止你一个人开发还要配合CI/CD流水线自动打包最佳方案是搭建一个简单的Nexus或Artifactory私有仓库然后把第三方SDK通过Web界面上传或者用mvn deploy:deploy-file推送上去。这样所有团队成员在pom.xml里配置好私服地址后都能像用中央仓库依赖一样正常引用打包、发布全链路都不会断。虽然搭建私服前期会花一点时间但长远看是省事的。4.5 外部JAR在运行期报ClassNotFoundException的排查顺序代码在IDEA里跑得好好的打成JAR后一到服务器就报ClassNotFoundException这种情况我遇到过无数次。排查思路按顺序来第一jar tf 你的包名.jar查看外部SDK的类是否真的在最终JAR里。如果不在回到打包方式上找原因多半是依赖没被插件识别。第二确认本地JAR的版本和服务器上运行的JAR版本一致。有时给客户的是旧包本地坑了半天新的SDK功能服务器却还在加载旧包。第三确认运行时有没有使用外部的CLASSPATH环境变量覆盖了JAR内部依赖。有些脚本里会写set CLASSPATHxxx这会导致JVM优先加载外部类出现预料之外的冲突。5. 高频问题排查实录打jar包路上我踩过的坑5.1 Lombok报错requires enabled annotation processing最近好几个朋友都在问这个问题IDEA里代码一切正常执行mvn clean package时却报错大概意思是当前环境没有启用注解处理而Lombok依赖又必须要注解处理才能生成代码。这个问题的根源是IDEA的Annotation Processors设置默认没有开启或者项目迁移到新电脑后配置丢了一部分。解决步骤是File → Settings → Build, Execution, Deployment → Compiler → Annotation Processors勾选Enable annotation processing然后点击Apply重新构建。如果项目里同时存在多个模块记得每个模块都要确认Annotations Profile里的配置正确。这个问题还有个变种Lombok版本和JDK版本不兼容。比如JDK 17之后老版本的Lombok1.18.20以下无法正常工作表现为编译报错或代码里使用的Slf4j生成的log变量全部不可见。升级Lombok依赖到较新版本或者统一JDK版本都能解决。5.2 打出来的JAR双击运行闪退Windows下双击JAR包运行闪退绝大多数不是打包的问题而是环境或执行方式的问题。先打开命令行切换到JAR目录手动执行java -jar xxx.jar这样能看到完整的报错信息。最常见的原因有三个一是Main-Class没配置或配错JVM提示Could not find or load main class二是JDK版本不匹配代码用了高版本特性但运行环境的JDK太老三是程序里引用了外部文件比如配置文件、模板目录双击运行时当前工作目录不确定程序按相对路径找不到文件直接退出。最后一种情况建议用java -jar xxx.jar配合--spring.config.location等外部参数启动不要依赖双击。5.3 打出来的JAR缺少第三方依赖如果你用的是IDEA图形界面的Artifact方式漏依赖是高频问题尤其当项目里通过Maven引入了七八个依赖时IDEA不一定能基于复杂依赖图生成完整的Fat JAR。稳妥的做法是给Maven项目配上maven-assembly-plugin或maven-shade-plugin。shade插件配置参考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 /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这个配置里有两个关键点。一个是ManifestResourceTransformer它会帮你在合并依赖时生成正确的Main-Class。另一个是filters里的排除签名文件逻辑加了之后基本不会碰上前面说的SecurityException。5.4 资源文件application.yml、数据库脚本等没有进入JARSpring Boot项目正常用mvn package打出来的JARapplication.yml等resources目录下的文件会自动打进BOOT-INF/classes。但如果你用的是IDEA的Artifact方式资源文件不会自动包含需要在Artifact配置的Available Elements里把resources目录手动添加进去否则运行时就报找不到配置文件。还有一种情况是资源文件打进去了但运行时加载的是外部配置而不是内部配置导致配置不生效。Spring Boot的配置加载规则里外部配置的优先级高于JAR内的配置。如果你在部署命令里写了--spring.config.locationfile:../config/application.yml那么JAR里同名的配置不会生效这不是打包的问题是配置优先级的问题排查时别搞混。5.5 MANIFEST.MF被锁定无法修改IDEA重新打包时偶尔会报Invalid jar file或者提示META-INF/MANIFEST.MF已经被占用。这通常都是IDEA缓存、或者是上一次打包时JAR文件没释放导致的。把out目录和target目录手动删除然后File → Invalidate Caches / Restart清一下IDEA缓存基本都能解决。5.6 一个项目里多个Main-Class时打出了错误启动类在微服务多模块项目里特别容易出现。Maven的spring-boot-maven-plugin在自动识别启动类时如果找到了多个候选可能打包成功但运行时不走你预期的那个入口。解决方式就是在插件配置里显式指定mainClass不要指望自动检测。5.7 服务器上运行提示“缺少主清单属性”这个报错英文是no main manifest attribute出现频率极高一般是因为你打的JAR只是一个普通JAR包MANIFEST.MF里没有Main-Class信息。Spring Boot的可执行JAR和普通JAR的差异就在这。排查方式用压缩软件打开JAR查看META-INF/MANIFEST.MF内容没有Main-Class就把包重新打一遍或者直接用Spring Boot插件打包。5.8 本地能跑服务器上就是不行的奇怪差别这类问题很多时候不是代码的问题而是环境不一致。本地Windows开发机和Linux服务器上文件路径分隔符不同Windows用\Linux用/如果代码里用字符串拼接路径到Linux上就容易找不到文件。另一个经典坑是字符集Windows默认GBKLinux默认UTF-8配置文件里有中文注释或中文内容时在服务器上读出来可能是乱码。建议项目里所有资源文件统一用UTF-8编码JVM启动参数里加上-Dfile.encodingutf-8。常见报错可能原因解决办法no main manifest attributeJAR不是可执行JARMANIFEST.MF缺少Main-Class检查打包方式使用Spring Boot插件或配置Main-ClassCould not find or load main classMain-Class配置错误或类名拼写不对在插件里显式指定正确的主类全限定名ClassNotFoundException/NoClassDefFoundError依赖没有包含在最终JAR中检查依赖作用域给外部JAR做install-file或使用shade插件SecurityException: Invalid signature file digest多个依赖的签名文件冲突打包时排除META-INF下的.SF/.DSA/.RSA文件Lombok编译报错注解处理未开启或Lombok与JDK版本不兼容Settings里勾选Enable annotation processing升级LombokJAR包双击闪退运行时环境问题或依赖缺失命令行运行查看报错检查JDK版本和工作目录6. 一点个人习惯分享最后说一个我自己的习惯。我早期也喜欢在IDEA里点各种图形界面按钮去Build Artifact后来项目越做越复杂、依赖越来越多图形界面就越发不好用了。现在我的标准流程基本固定成了代码写完、本地跑通然后在IDEA的Terminal里敲mvn clean package -DskipTests再用java -jar验证一次最后把JAR丢到服务器上。中间偶尔需要引入本地SDK就先把SDK安装到本地Maven仓库再在pom.xml里加依赖整个过程非常机械、不容易错。如果你也是刚接触IDEA的Java开发者建议也尽早切换到这种“Maven生命周期 命令行验证”的工作流上。IDEA的图形界面当然可以用但只建议用在纯Java小工具项目上。至于那些网上流传的“一键打包神器”“某某版本特殊激活方式”我劝你别花时间折腾IDEA社区版Community Edition在官方渠道就能免费下载日常开发、打包、跑Maven项目完全够用把精力放在熟悉构建工具和排查实际问题上去长期看收益大得多。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门