简介:面向 Java 开发者与 Eclipse 使用者的实操教程,专门解决将 Java 工程导出为可执行 JAR 文件、并正确包含第三方依赖的问题。资料以 PDF 文档呈现,共 1 个文件,压缩包大小仅 197KB,便于下载后随时翻阅。教程从 Eclipse 的 Export 向导讲起,重点演示 Runnable JAR file 的导出方式,并说明处理第三方 Jar 包的推荐做法;同时特别提醒打包后 Run Configuration 中设置的 JVM 参数不再生效,需要以启动命令为准,并给出配置文件 conf 目录的放置方法,以及利用 run.bat 一键启动的简化方案。对于经常需要部署爬虫脚本、后台服务或独立工具程序的开发场景,这份资料能帮助读者减少反复试错,快速完成可执行 JAR 的打包与分发。目前已有 2021 人学习下载,适合刚开始接触打包、需要处理多模块依赖配置的 Java 学习者参考。
1. Eclipse 里导出的 Jar 跑不起来?问题多半不在 Eclipse 身上
用 Eclipse 导出可执行Java工程/可执行Jar文件(包含第三方Jar包),是大多数 Java 开发者从"能写代码"走向"能交付工具"时绕不开的一道坎。现象往往一致:IDE 里右键 Run 顺风顺水,Export 完成后在命令行敲 java -jar,却报"找不到或无法加载主类",或者跑不了几行就抛 ClassNotFoundException。多数人第一反应是怪 Eclipse 的导出功能难用,实际上翻车点集中在三个地方:主类没有正确写进清单文件、第三方依赖没被打进去、依赖被解压合并后出现同名类覆盖。这篇笔记沿着"工程准备、导出配置、依赖策略、踩坑排查、命令验证"的顺序,把每条路径的参数和副作用一次讲清楚,适合正在用 Eclipse 写 Java 工具、需要把成果交付给同事运行的人。
为什么强调"包含第三方 Jar 包"?因为只导出自己的代码,任何 IDE 都做得很好;真正的分水岭就在依赖处理上。Eclipse 给了三个选项,翻译得有些含糊,很多人选错后拿到的是外表完整、内里残缺的 Jar。下面从打包前要确认的三件事讲起。
2. 导出前的工程准备:先认清主类、清单文件和第三方依赖
2.1 主类到底是什么:Runnable 类的判定标准
在打开导出对话框之前,第一件事是确认工程里存在一个能被 JVM 直接调用的入口。所谓"可执行 Java 工程",编译后的产物里必须有一个包含正确签名 main 方法的类。Eclipse 判断"可执行"的依据就是这个方法,不是类名,也不是工程名。
一个合法的入口长这样:
package com.example.report; public class ReportGenerator { // 这才是 java -jar 能识别的入口 public static void main(String[] args) { System.out.println("report tool start"); } }这里的三个修饰符 public、static、void 一个都不能少。如果 main 方法是 private,或者缺少 String[] args 参数,Eclipse 的 Launch configuration 里就不会出现这个类,导出界面会直接提示"没有可用的启动配置"。还有一种常见写法是把 main 放到非 public 类里,编译没问题,但 Java 规范要求启动类本身必须是 public,否则某些 JVM 实现会报"主类找不到",而 IDE 里 Run As 却能跑通,这种不一致最容易误导人。
我自己的习惯是,在导出前先做一次双确认:在包资源管理器中选中目标类,右键 Run As -> Java Application,如果能正常启动,才继续走导出流程。这个动作不值钱,但能排除掉 50% 的"导出后跑不起来"问题。
另外,main 方法所在类尽量不要放在默认包里,也就是不声明 package 的那个包。Eclipse 能编译,MANIFEST.MF 里写"Main-Class: MainApp"也能跑;但一旦你的代码和某个第三方 Jar 存在同名类,默认包类在类加载时的优先级就会变得很不可控。哪怕工具只有两个文件,我也建议建一个 com.xxx.util 之类的最小包名,这个习惯能省掉很多类加载层面的玄学问题。
2.2 MANIFEST.MF:Main-Class 和 Class-Path 才是执行的核心
Jar 文件本质是一个 ZIP 包,只是多了 META-INF/MANIFEST.MF 清单文件。java -jar 启动时,JVM 先读这个清单文件,找到 Main-Class 指定的类,再把 Class-Path 里声明的 Jar 加入运行时类路径。Eclipse 导出向导会自动生成清单文件,但理解它的格式才能排查问题。
一个典型的 MANIFEST.MF 长这样:
Manifest-Version: 1.0 Main-Class: com.example.report.ReportGenerator Class-Path: lib/commons-io-2.11.0.jar lib/gson-2.10.1.jar这里有两个格式要点。第一,Class-Path 里多个 Jar 之间用空格分隔,不是逗号也不是分号;路径是相对于 Jar 文件所在目录的相对路径。第二,清单文件每行最长 72 字节(含换行符),超过就必须续行,续行以空格开头。Eclipse 自动生成时会把长行拆成多行,这是合法格式;但如果有人用记事本手动拼,很容易在行尾多一个空格或少一个换行,启动时直接报错。
还有一个容易被忽略的编码问题。清单文件的写入编码在不同 JDK 版本上行为不一致,如果你的类名或路径里含中文,且工程的编译编码不是 UTF-8,导出的 Jar 换台机器就可能"找不到主类"。所以导出前我会先看一眼工程的 Resource 属性,确认 Text file encoding 是 UTF-8,而不是继承平台默认的 GBK。这一点在避坑章节还会再提。
2.3 梳理第三方依赖:Maven 工程和普通工程各自的做法
导出可执行 Jar 前,得先知道工程到底依赖了哪些第三方 Jar。依赖来源不同,处理方式完全不同。
如果是 Maven 工程,依赖清单在 pom.xml 的 里。Eclipse 的 Runnable JAR file 向导能自动读取 Maven 依赖并参与打包,但前提是本地的 Maven 仓库状态是干净的。我常用的确认命令是:
mvn dependency:tree这条命令会把所有依赖的传递关系列出来,比在 Eclipse 界面里一个个点开看更完整。mvn dependency:tree 输出里能看到每个依赖的 groupId、artifactId、version 和 scope。你真正要关注的是 scope 为 compile 和 runtime 的依赖,这两类需要进 Jar;scope 为 provided 的(比如 servlet-api)由运行环境提供,打进去反而可能引发类冲突。如果你看到 optional 标记的依赖,注意 optional 不影响传递,但 Eclipse 不一定把它打进去,运行到相关功能时才报 NoClassDefFoundError。
如果是普通 Java 工程,没有 pom.xml,第三方 Jar 一般集中在工程的 lib 目录。这里有个高频翻车点:把 Jar 拖进 lib 文件夹并不等于加入了 Build Path。Eclipse 编译时只看 Java Build Path 里配置的类库,不在 Build Path 里的 Jar 对工程不可见,导出时自然也不会包含。验证方式是在 Project Explorer 里展开工程的 Referenced Libraries 节点,看里面是否列了你放进去的 Jar;没有的话,右键工程 -> Build Path -> Configure Build Path -> Libraries -> Add JARs 或 Add External JARs,选中对应文件后再导出。
还要区分一个概念:编译期依赖和运行时依赖。有些 Jar 只是编译期需要,比如代码生成器和注解处理器,运行时用不到;有些则是运行时通过反射动态加载的,编译期根本没有直接引用。Eclipse 的导出向导不区分这两者,它按 Build Path 里的清单全部打包。所以如果你发现导出的 Jar 体积异常大,多出来的往往是编译期才用的东西;反过来,如果运行时报 ClassNotFoundException,优先检查那个类在哪个 Jar 里,以及那个 Jar 是否真的在 Build Path 中。
3. Eclipse 导出可执行 Jar 的操作路径与三种"库处理方式"
3.1 选对入口:Runnable JAR file 而不是 JAR file
Eclipse 的 File -> Export 里有好几个和 Java 相关的选项,很多人一路点到 Java -> JAR file,生成一个普通 Jar 就交付,结果必然跑不起来。普通 JAR file 导出只做一件事:把编译后的 class 文件打成一个包,不写 Main-Class,也不处理第三方依赖。真正生成可执行 Jar 的入口是 Java -> Runnable JAR file,这个名字直译就是"可运行的 Jar"。
完整操作路径如下:
- 在包资源管理器中选中工程根节点,不是选中某个类。
- 菜单栏 File -> Export...,弹出窗口搜索框里输入 Runnable 三个字母,选中 Runnable JAR file,点 Next。
- Launch configuration 下拉框里选择之前 Run As 过的启动配置。列表为空说明工程里没有可运行的 main 方法,回上一章检查入口类。
- Export destination 填输出 Jar 的完整路径。我一般输出到工程外部的 dist 目录,避免把 Jar 写进工程的 bin 或 target 目录,造成输出互相覆盖的错觉。
- Library handling 单选组里选择依赖处理方式,这个在 3.2 详细展开。
- 如果勾选 Save as ANT script,Eclipse 会生成一个 ant 脚本,把这次导出配置固化下来。以后改了一点代码,可以直接命令行执行 ant 脚本重新打包,不用再点一遍界面。这个选项目前不常用,但值得知道。
很多 Eclipse 使用教程只会教你 Export -> JAR file 把 class 存出来,那对交付一个可执行程序帮助不大。判断方法很直接:导出完成后,右键 Jar 用解压软件打开,看里面有没有 META-INF/MANIFEST.MF,以及清单文件里有没有 Main-Class 这一行。没有就是选错了入口。
3.2 Library handling 三选项到底在干什么
Runnable JAR file 导出界面里有一组单选按钮,是整个流程里最核心的决策点。这三个选项名字翻译得有点绕,我按实际行为解释:
第一项 "Extract required libraries into generated JAR":把每个第三方依赖里的 class 文件全部解压,再合并进你生成的 Jar。最终产物只有一个文件,交付最方便。缺点是依赖的 class 全部被拆散,Jar 内既不保留原始 Jar 的结构,也无法区分哪些 class 来自哪个依赖。如果多个依赖里存在同名类,后处理的那个会覆盖先处理的,程序运行到某个类时到底加载哪个版本,完全取决于 Eclipse 内部的处理顺序。
第二项 "Package required libraries into sub-folders":名字听起来是把依赖 Jar 原封不动放进子目录,实际不是。它仍然是把依赖的 class 解压出来,只不过统一放进生成 Jar 内的 lib/ 子目录。最终产物还是一个自包含 Jar,但内部结构是平铺的,没有任何隔离能力。后面避坑章节会说,这个选项解决不了同名类覆盖问题。
第三项 "Copy required libraries into a sub-folder next to the generated JAR":这个选项不碰依赖 Jar 的内部内容。Eclipse 把工程自己的 class 打成主 Jar,在输出路径旁边生成一个 lib 文件夹,把依赖 Jar 原样复制进去,然后在 MANIFEST.MF 里写 Class-Path。这是三种方式里最容易排查问题、最接近"原样交付"的一种。
这里有一个很容易误解的点:第二项的"内部 lib 子目录"和第三项的"外部 lib 目录"有本质区别。内部 lib 子目录对 JVM 加载没有特殊含义,class 还是按包路径整体加载的;外部 lib 目录则对应 Class-Path 的相对路径,JVM 按清单文件指过去加载原始 Jar。选错参数再去看网上那些只说"导出时选第二项就够了"的教程,很难对得上号。
3.3 三种导出方式的产物对比
把三种方式导出的产物结构摆在一起看,更直观:
| 处理方式 | 产出物 | Jar 内部结构 | 运行时额外依赖 | 典型使用场景 |
|---|---|---|---|---|
| Extract required libraries into generated JAR | 单个 Jar | 第三方 class 散落在根包路径下 | 无 | 纯工具类、无签名依赖、项目简单 |
| Package required libraries into sub-folders | 单个 Jar | 第三方 class 在 lib/ 子目录下 | 无 | 希望 Jar 内部结构清晰一点 |
| Copy required libraries into a sub-folder | Jar + 旁路 lib/ 目录 | 不含第三方 class | 需要 lib 目录与 Jar 同级 | 有签名依赖、需要人工排查类冲突 |
参数选择上没有绝对正确,我的默认建议是:能接受单个文件交付就选第一项,但前提是依赖里不能有签名 Jar,且不会出现同名类冲突;项目依赖稍微复杂一点,立刻换第三项,宁可交付时多带一个目录,也不要后期被类冲突折磨。
第一项还有一个隐藏问题:有些依赖 Jar 自带 META-INF/services 文件,里面声明了 SPI 服务实现。解压合并后,多个 Jar 的 services 文件会互相覆盖,导致运行时 ServiceLoader 找不到对应的实现类,程序不报编译错,运行到某个功能时才知道挂了。这个问题下文避坑章节会重点展开,这里先记住一个结论:合并越多依赖,资源文件冲突的概率越大。
4. 第三方 Jar 包怎么打进去:三种策略的完整对比与操作细节
4.1 策略一:解压合并,适合追求单个交付文件
方案 A 的效果在前一章已经说了,把所有依赖的 class 全部复制进你的 Jar。Eclipse 在导出时逐个扫描依赖 Jar,按包路径复制 class 文件,最终把几千个类揉在一起。对使用者来说,交付物是一个文件,双击或命令行一把就跑,体验最好。
但它有两个明确的副作用。第一是类冲突:如果两个第三方 Jar 里出现同一个全限定类名,Eclipse 按 Build Path 顺序覆盖,后处理的覆盖先处理的。你的程序可能运行到一个"不是你预期实现"的类上,表现是行为诡异,不报错。第二是资源文件覆盖:class 文件之外,依赖 Jar 里若有同名配置文件(比如 spring.factories、META-INF/services/xxx),同样会被覆盖,SPI 机制直接失效,ServiceLoader 跑不出来。
应对办法只有一个:接受不了这两个风险,就退回方案 C。如果坚持用方案 A,导出后第一时间检查重复条目:
jar tf report-tool.jar | grep "META-INF/services" | sort | uniq -d这条命令会把 Jar 内所有 META-INF/services 目录下重复出现的文件名列出来。有重复,说明有服务描述文件被覆盖了,运行结果大概率不符合预期。这里的逻辑是:sort 让相同的路径相邻,uniq -d 只输出出现超过一次的行。看到输出后,如果确认哪些依赖声明了服务,回到方案 C 重新导出,否则继续排查类冲突。
4.2 策略二:内部 lib 子目录,视觉效果大于实际收益
方案 B 的界面描述是 "Package required libraries into sub-folders",我实际用下来发现它解决的问题非常有限。它把依赖 class 解压后放进 Jar 内的 lib/ 子目录,但 JVM 的类加载机制不关心 class 在 Jar 内的物理位置,只关心 classpath 顺序,所以这个目录差异对运行没有影响。唯一的好处是打开 Jar 时能大致看出哪些 class 来自第三方,仅此而已。
如果你指望通过"Jar 内 lib 子目录"实现依赖隔离,比如两个依赖版本冲突,这个方案做不到。所有 class 最后都在同一个 classpath 上,同名类照样互相覆盖。真正的隔离只能靠独立的 ClassLoader,那已经超出了 Eclipse 导出能解决的范畴。
这个方案最常坑人的点是 getResourceAsStream 读不到配置文件。原本在某个依赖 Jar 根目录下的 config.properties,解压合并后路径还是那个路径,理论上应该能读到;但如果另一个依赖里也有一份同名配置文件,覆盖顺序决定你读到的内容可能是错的。我见过一个项目用方案 B 导出的 Jar,中文乱码查了半天,最后发现是多个依赖里的 messages.properties 互相覆盖,加载到的资源根本不是当前代码要的那个。
我的总结是:方案 B 可以当成"排得整齐一点的方案 A",但不要指望它有隔离能力。选它不会比方案 A 更安全,反而会让你对内部结构产生错误预期。
4.3 策略三:外部 lib 旁路,最稳定但交付时要带目录
方案 C 是三者中唯一保留第三方 Jar 原样的方式。Eclipse 生成你的主 Jar 时,只输出工程自己的 class,然后到目标路径下创建 lib/ 文件夹,把依赖 Jar 复制进去,最后在 MANIFEST.MF 里写 Class-Path: lib/a.jar lib/b.jar 这样的相对路径。
这里最关键的一点是:Class-Path 里的路径是相对 Jar 文件位置的相对路径,不是相对当前工作目录。也就是说,不管你在哪个目录执行 java -jar /path/to/app.jar,JVM 都会自动去找 /path/to/lib/ 下的依赖。但如果有人只把 Jar 拷走而漏了 lib 目录,就会报 ClassNotFoundException,报错信息里能清楚看到缺哪个类。
这个方案是三种里最可预测的:问题定位简单,缺哪个类直接去 lib 目录找对应 Jar;类冲突概率低,因为 Jar 还是原来的 Jar,没有被解压合并。代价是交付物从"一个文件"变成"一个目录"。如果目标机器是内网隔离环境,还要额外注意 lib 目录里所有 Jar 的路径不能含空格,否则部分脚本解析 Class-Path 时会出错。
方案 C 也有自己的坑:Eclipse 不会递归处理传递依赖。比如你引入 A.jar,A.jar 内部依赖 B.jar,但 B.jar 没有出现在工程的 Build Path 里,Eclipse 只会复制 A.jar,不会自动把 B.jar 拉进来。运行 A 的代码时,JVM 加载 A 的类,A 内部引用的 B 的类找不到,异常信息指向 B 的某个类,很多人误以为是 A.jar 本身有问题。遇到这种传递依赖场景,我一般先用 mvn dependency:tree 把依赖树拉出来,看哪些 Jar 不在 Build Path 里,然后手动加进去。
4.4 手动调整 MANIFEST.MF:当 Class-Path 顺序需要干预时
如果依赖特别多,Eclipse 生成的 Class-Path 顺序不一定符合你想要的加载优先级。比如两个依赖里都有同一个类,你想强制让某个版本生效,可以导出后用文本编辑器打开 Jar 内的 MANIFEST.MF,手动调整 Class-Path 里 Jar 的排列顺序,然后用 jar 命令回写:
jar ufm report-tool.jar MANIFEST.MF这条命令中 u 表示更新,f 指定 Jar 文件,m 表示使用外部清单文件。更新后,MANIFEST.MF 里的内容会整体替换 Jar 内原有的清单文件。注意这里的 MANIFEST.MF 是你在外部编辑好的文件,不是从 Jar 里解压出来的那份,两者很容易搞混。
手动编辑时的格式要求很严格:每行 72 字节以内,超过要续行,续行以空格开头;文件最后要有空行。很多人用记事本编辑完,最后一行没有换行符,jar 命令不报错,但 JVM 在解析时可能把最后一行忽略,偏偏那一行写的是 Main-Class,然后你又回到"找不到主类"的循环里。所以在保存后,我会用编辑器开启显示换行符,确认文件末尾确实有一个空行。
5. Eclipse 导出 Jar 避坑:5 个高频问题与排查思路
5.1 报错"找不到或无法加载主类":先查清单再查环境
现象:在命令行执行 java -jar app.jar,输出 Error: Could not find or load main class com.example.MainApp;Java Web 场景下可能是"找不到或无法加载主类 org.apache.catalina.startup.Bootstrap",本质上同一个问题。
原因分三类:一是 MANIFEST.MF 里的 Main-Class 写错或格式坏;二是 main 类确实不在 Jar 里,比如主类所在包没被编译或被排除了;三是 JDK 版本不匹配,用高版本 JDK 编译的 class 文件在低版本 JVM 上加载失败,报错信息和"主类加载不了"一样。
解决按顺序来。先用 unzip 看清单:
unzip -p app.jar META-INF/MANIFEST.MF把 Main-Class 和 Class-Path 两行读出来,人工核对全限定类名和包名是否一致。再去确认类文件在不在 Jar 里:
jar tf app.jar | grep "MainApp"最后在目标机器上执行 java -version,对比编译工程的 JDK 版本。注意目标机器上 java 环境变量配置是否正确也会影响结果,命令能执行不代表用的是你预期的那份 JDK,用 which java 看一下实际解析路径。
5.2 运行中抛 ClassNotFoundException:第三方依赖根本没进去
现象:程序启动几秒后才报 java.lang.ClassNotFoundException: org.apache.commons.io.IOUtils,而不是启动瞬间报错。
原因:要么导出时进错了入口,选了普通 JAR file 而不是 Runnable JAR file;要么依赖没有出现在工程的 Build Path 里;要么方案 C 导出后 lib 目录漏带。
解决:打开导出对话框,确认自己进的是 Runnable JAR file;在 Project Explorer 的 Referenced Libraries 里确认依赖在不在;如果是方案 C,检查 Jar 同级目录下有没有生成 lib/,lib/ 里有没有对应 Jar。这个坑的特点是报错越晚越隐蔽,说明类是被反射或者延迟加载的,排查时不要只盯着启动日志,把完整堆栈打出来,看第一个"找不到"的类名对应哪个依赖。
5.3 解压合并后同名类覆盖,行为变得不可预测
现象:导出时选了解压合并,程序运行不报错,但功能表现怪异,比如日期格式化结果不对、JSON 序列化字段缺失。这种问题最折磨人,因为没有异常信息。
原因:两个第三方 Jar 里有同名类,Eclipse 按 Build Path 顺序覆盖,后处理的覆盖先处理的。运行时会加载到哪个版本取决于导出时的处理顺序,和你代码编译时看到的那个类不一定一致。
解决:把两个冲突的 Jar 找出来,用反编译 jar 的工具比如 JD-GUI 打开,逐个确认里面是否存在相同的全限定类名。然后回到方案 C,保留 Jar 原样;或者在 Build Path 的 Order and Export 标签页手动调整顺序,把你想优先加载的版本排在前面。如果调整顺序还不够,把冲突的依赖从 Build Path 移除,改成手动 Add External JARs 的方式,彻底绕开 Eclipse 的合并逻辑。
5.4 中文路径、空格目录和系统编码引发的导出失败
现象:Eclipse 装在 D:\开发工具\eclipse,工程在 D:\项目\report-gen,导出向导能走完,但生成的 Jar 在别的机器上运行报"找不到主类",且报错信息里的类名中文显示成乱码。
原因:清单文件里的类名和 Class-Path 路径经过平台默认编码写入。如果工程编译编码是 GBK,而目标机器的区域设置不同,JVM 解析清单文件时就会失败。这里的编码问题只影响字符串内容的写入方式,和 class 文件本身无关,所以在本机能跑、换机器就挂。
解决:导出前在工程属性里确认编码为 UTF-8,路径是 Project -> Properties -> Resource -> Text file encoding。同时确认 pom.xml 或 build 脚本里没有硬编码 -Dfile.encoding=GBK 之类的参数。如果工程路径实在绕不开中文,把工程整体复制到 C:\work 这样纯英文目录再导出一次,对比结果。这个坑不是玄学,是清单文件写入编码和文件系统编码双重作用。
5.5 Maven 工程导出时依赖不完整:provided 作用域与本地缓存
现象:pom.xml 里明明有依赖,Eclipse 导出后 Jar 里没有对应类,运行时报"找不到 Spring 相关类";或者 Eclipse 菜单导出时报 An internal error occurred during: "Updating Maven project"。
原因:Eclipse 的 Runnable JAR file 导出依赖的是 Maven 的 classpath,而不是 pom 的原始内容。pom 里声明为 provided 或 test 作用域的依赖不会进 Jar;本地仓库版本和 pom 不一致时,Eclipse 使用的 classpath 是过期的;另外 workspace 缓存损坏也会导致 Update Maven project 报内部错误。
解决:在 Eclipse 里右键工程 -> Maven -> Update Project,勾选 Force Update of Snapshots/Releases 强制刷新。然后再看 Maven Dependencies 节点里实际列出的 Jar 和 pom 是否一致。provided 依赖由运行环境提供,比如 servlet-api 在 Web 容器里有,本来就不该打进去。遇到 Updating Maven project 内部错误,多半是本地 .m2 里对应依赖的元数据损坏,删掉该目录重新依赖下载就行;不建议直接删 .metadata,那样会把整个工作区的设置都清掉,代价太大。
6. 验证可执行 Jar 的三个命令和后续维护建议
导出不是终点,验证才是。我对每一个导出的 Jar 都会跑三条命令,顺序固定,缺一不可。
第一条是体检,确认 Jar 内部结构符合预期:
jar tf report-tool.jar | head -50看输出列表里有没有 META-INF/MANIFEST.MF、有没有你自己的主类 class 文件、依赖是否按预期出现。如果选了方案 C,这一步还会看到 lib/ 目录整洁地区分第三方内容。jar tf 的输出就是 Jar 内部路径,和 ZIP 的条目列表一致,用 unzip -l 看效果一样。
第二条是核对清单文件:
unzip -p report-tool.jar META-INF/MANIFEST.MF输出后人工核对 Main-Class 和 Class-Path 两行。我在避坑章节的排查顺序里先用了它,验证时也一样。很多人跳过这一步直接跑,结果了半天依赖问题,最后发现是 Main-Class 行尾缺了一个换行符。
第三条才是真正的运行验证:
java -jar report-tool.jar --help或者带业务参数跑一次冒烟测试。注意这里的当前工作目录是敲命令时所在的目录,不是 Jar 文件所在目录。程序里如果用了相对路径读配置文件,就会在这里踩坑;建议在代码里用 ReportGenerator.class.getProtectionDomain().getCodeSource().getLocation() 取 Jar 实际位置,再拼接相对路径,这比依赖用户从哪个目录启动更可靠。
验证通过后,维护层面的习惯我建议尽早培养:只要这个工程还会迭代,就把打包从 Eclipse 手动导出迁到 Maven 的 maven-assembly-plugin 或 shade 插件。原因不是 Eclipse 做不了,而是手工导出的配置不可追溯,代码仓库里无法复现一次导出的全过程。我自己就有过血泪经验,一个工具用 Eclipse 导出交付,三个月后再改,原来的 Launch configuration 已经被删了,重新配出来的 Jar 行为竟然不一样。用插件定义打包,等于把过程固化进仓库,才是长久的后悔药。
最后一个习惯是跨环境验证。如果工具要跑在 ARM 架构的机器上,或者目标机器只有 JRE 没有 JDK,提前在对应环境跑一遍 java -version 和 java -jar。类文件版本错误通常只在目标环境暴露,本地永远复现不了。检验过程保持这套顺序,Eclipse 导出可执行 Jar 就不再是黑匣子了。希望这些经验帮到你,少踩一次是一次。
本文还有配套的精品资源,点击获取