Eclipse导出可执行Jar包:第三方依赖打包与部署排查指南
简介针对 Eclipse 中 Java 工程打包难题这份 PDF 教程面向需要生成可执行 Jar 文件、并希望包含第三方依赖库的 Java 开发者尤其适合从 IDE 直接运行转向命令行部署的爬虫或服务端项目。教程围绕 Eclipse 的 Runnable JAR file 导出方式展开重点说明第三方 Jar 包三种处理方式的差异及默认解压整合到 src 目录的细节指出 JVM 参数在打包后失效的原因并完整演示如何放置 conf/xx.properties 配置文件、通过 java -Xmx256m -Xms128m -jar spider.jar 命令启动以及编写 run.bat 批处理脚本以便双击运行和修改启动参数。资源包仅 1 个 PDF 文档大小 197KB单文件便于快速查阅。已有 2021 人学习浏览适合初学打包或准备部署到新虚拟机的开发者参考。1. Eclipse 导出可执行 Jar 文件为什么在 IDE 里能跑的程序打包后问题不断Eclipse 导出可执行 Jar 文件这件事通常不是开发期冒出来的需求而是部署期才逼着你解决。我之前维护的爬虫工程一直直接跑在 Eclipse 里写完代码点一下运行出问题就切回去改省事是真省事。直到师兄部署了新虚拟机要求把爬虫代码打成包含第三方 Jar 包的可执行 Jar 文件迁过去我才发现那些在 IDE 里跑得顺顺当当的程序离开 Eclipse 之后全是问题System.out.println 不知道该往哪儿打、数据库连接和路径参数写死在代码里、依赖的第三方 Jar 包散得到处都是。折腾了一下午最后靠 Eclipse 自带的 Runnable JAR file 导出选项解决了。这篇笔记就把完整的操作流程和踩过的坑记录下来给同样需要在 Eclipse 中导出可执行 Jar 文件包含第三方 Jar 包的读者做个参考。2. 打包前的工程整理log4j 替换 System.out、配置外置、路径统一直接把 Eclipse 工程 Export 成 Runnable JAR file 并不复杂但如果代码里全是 System.out.println 和硬编码路径那么打包成功率虽然高运行起来却处处是坑。我这次迁移爬虫动手导出之前做了三件事日志替换、配置外置、路径与连接参数集中管理。这三件事单独看都不起眼组合在一起决定了 Jar 包脱离 Eclipse 之后能不能正常存活。2.1 用 log4j 替换 System.out.println把日志输出从控制台搬到文件打包成 Jar 之后程序大多跑在无头环境里——没有 Eclipse 控制台没有 IDE 的 stdout 面板System.out.println 打在屏幕上的内容要么丢进 nohup.out要么直接消失。等发现爬虫某天凌晨挂掉的时候什么日志都没留下只能干瞪眼。所以日志这件事必须在打包之前解决。我一般会在工程 src 目录下放一个 log4j.properties这里以 log4j 1.x 为例# 根日志级别 INFO同时挂 file 和 console 两个输出 log4j.rootLoggerINFO, file, console # console 负责开发期在 Eclipse 里看日志 log4j.appender.consoleorg.apache.log4j.ConsoleAppender log4j.appender.console.TargetSystem.out log4j.appender.console.layoutorg.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern%d{yyyy-MM-dd HH:mm:ss} [%p] %c{1} - %m%n # file 负责部署期落盘按天切割 log4j.appender.fileorg.apache.log4j.DailyRollingFileAppender log4j.appender.file.Filelogs/spider.log log4j.appender.file.DatePattern.yyyy-MM-dd log4j.appender.file.layoutorg.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern%d{yyyy-MM-dd HH:mm:ss} [%p] %c{1} - %m%n逻辑说明rootLogger 级别设成 INFO同时挂上 file 和 console 两个 appender。console 管开发期在 Eclipse 里看file 管部署期落盘。DailyRollingFileAppender 按天切分日志文件每天生成一个 spider.log.2017-08-01 格式的文件不会让单个日志膨胀到几个 GB。参数说明File 里的路径 logs/spider.log 是相对路径相对于 java -jar 命令的执行目录。后面运行 Jar 时会用到建议部署时先建好 logs 目录否则 logger 初始化时可能因为目录不存在而静默失败。配好之后把代码里的 System.out.println(begin crawl...) 替换成 log.info(begin crawl...)看着是苦力活但值得。2.2 配置文件外置conf/xx.properties 别放进 Jar 包内部很多工程的配置文件放在 src 下导出时会跟着打进 Jar 包。这样做的最大问题是部署环境换了数据库密码、目标 URL 这些参数要改的时候你必须重新打包再传一次。我一般先把配置外置到 conf 目录代码里用文件系统读取import java.io.*; import java.nio.charset.StandardCharsets; import java.util.Properties; public class Config { private static final Properties PROPS new Properties(); static { try (InputStreamReader reader new InputStreamReader( new FileInputStream(getConfigPath()), StandardCharsets.UTF_8)) { PROPS.load(reader); } catch (IOException e) { throw new RuntimeException(配置文件加载失败请确认 conf/spider.properties 存在, e); } } // 优先用启动参数 -Dconf.dir 覆盖配置目录 private static String getConfigPath() { String confDir System.getProperty(conf.dir, conf); return confDir File.separator spider.properties; } public static String get(String key) { return PROPS.getProperty(key); } }逻辑说明读取顺序是先看启动参数有没有 -Dconf.dir有就用它拼路径没有就落到相对于当前工作目录的 conf/spider.properties。刻意不用 getResourceAsStream 从 classpath 读就是为了让配置文件留在 Jar 外面部署时可以随便改。参数说明conf.dir 不是 Java 标准库定义的属性是打包者自定义的约定运行时通过 -Dconf.dir/opt/spider/conf 传入。读取时指定 UTF-8 是防止 properties 里出现中文路径或中文注释乱码properties 文件的默认编码是 ISO-8859-1这一步几乎是必踩的坑。2.3 数据库连接与路径参数集中配置换环境只改一个文件爬虫代码里最常见的硬编码就是数据库连接和下载目录。我有段时间一个工程里散落了 db.properties、path.properties 好几个配置后来合并成一个 spider.properties键名用前缀区分db.urljdbc:mysql://localhost:3306/spider?useUnicodetruecharacterEncodingutf8 db.usercrawler db.passwordyour_password download.dir./downloads download.threads4 request.timeout5000代码里对应的读取方式String dbUrl Config.get(db.url); int threadCount Integer.parseInt(Config.get(download.threads));db.user、db.password 从配置文件读换环境时只需要替换一个文件。download.threads 是整型字符串转数字时要考虑缺省值——如果配置文件漏了这一项Integer.parseInt(null) 直接抛 NumberFormatException所以更稳妥的写法是给 get 方法加默认值比如Config.get(download.threads, 4)。整理完这些再往下走导出流程心里就有底了。3. 打包方案选型MANIFEST 手写、Fat Jar、Runnable JAR file 怎么选Java 工程打成可执行 Jar 的办法不少网上资料一搜一大把但真正靠谱的其实就那么几条路手写 MANIFEST.MF、用 Fat Jar 插件或 Maven Shade、用 Eclipse 自带的 Runnable JAR file。选错方案会在部署阶段反复折腾所以我先说结论再展开对比。3.1 手工配置 MANIFEST.MFClass-Path 的相对路径与维护成本Jar 包本质上是个 zip 文件META-INF/MANIFEST.MF 里写了 Main-Class 和 Class-Path 两个关键值java -jar 就是根据它们找到入口类、定位依赖。手写一份大概长这样Manifest-Version: 1.0 Main-Class: com.crawler.Main Class-Path: lib/httpclient-4.5.13.jar lib/jsoup-1.14.2.jarMain-Class 必须写成类的全限定名不带 .class 后缀Class-Path 里每个 jar 都是相对于当前 jar 位置的相对路径多个依赖用空格分隔。问题在于路径写错一个字母启动时抛 NoClassDefFoundError报错信息只给类名不给 jar 路径排查全靠猜。而且依赖升级时得手动编辑 MANIFEST 再重新打包漏一步就翻车。如果你确实想走手写这条路可以这样打javac -encoding UTF-8 -d classes src/com/crawler/*.java jar cfm spider.jar MANIFEST.MF -C classes .javac 把源码编译到 classes 目录jar 用 cfm 参数把 MANIFEST.MF 写进包并包含所有 class。这个流程能跑通但依赖超过五个之后维护成本就明显高于收益了适合依赖一两个的小工具。3.2 Fat Jar 插件与 Maven Shade功能全但引入了新复杂度Eclipse 老用户应该听说过 Fat Jar 插件它能把依赖解压合并成一个“胖 Jar”打出来单文件可跑不用带外置 lib 目录。缺点是这插件停止维护很久了新版 Eclipse 基本装不上只能在旧环境里用。如果你今天刚开始接触这个问题不建议在这个方向上浪费时间。Maven 工程更常见的做法是用 shade 插件在 pom.xml 里配置 plugin 之后执行 mvn package 就能生成包含依赖的 jar。但前提是工程得先转成 Maven 结构补上 pom.xml把每个依赖的 groupId、artifactId、version 都查清楚。对一个运行了两三年的纯 Eclipse 工程做这种迁移成本远比收益高。我的建议是除非你本来就打算把工程管理迁到 Maven否则不要为了打包而引入 Maven。3.3 Runnable JAR fileEclipse 原生功能适合纯 Eclipse 工程Runnable JAR file 是 Eclipse 导出向导里的一个选项位于 Export 对话框的 Java 分类下。它的原理是读取你在 Eclipse 里运行过的 Launch Configuration把该配置涉及的工程 classpath 项全部收集起来再按你选择的依赖处理方式输出成可执行 Jar。这个方案的优势很直接不用装插件、不用写 MANIFEST、不用转 Maven 结构。限制也很明确它依赖 Eclipse 环境本身如果工程从来没在 Eclipse 里运行过 main 方法Launch Configuration 下拉框是空的这个功能就用不上。快速验证方法确保工程至少有一次 Run as Java Application 的运行记录再打开 Run Configurations 看一眼里面有对应的 Application 配置就没问题。4. 导出可执行 Jar 的操作流程Launch Configuration 与三种依赖处理方式选 Runnable JAR file 之后操作本身并不复杂但有一两个细节会影响最终效果。下面按我实际操作时的顺序展开。4.1 导出向导从右键 Export 到 Runnable JAR file在 Project Explorer 里选中 Java 工程右键 Export…在弹出的对话框里展开 Java 分类选择 Runnable JAR file点 Next在 Runnable JAR File Export 页面关键设置有两处Launch Configuration 和 Export destinationLaunch Configuration 下拉框里列出当前工作空间所有可用的 main 方法配置选一个入口类比如 com.crawler.Main。如果这个工程从来没运行过这里会是空的必须先运行一次 main 方法让 Eclipse 生成配置。Export destination 填写要生成的 jar 文件名比如 D:/deploy/spider.jar。目录最好先建好Eclipse 不会帮你创建路径。如果只想导出部分资源可以在导出页面的其他选项里勾选不过大多数情况保持全量导出就行。4.2 Library Handling 三个选项Extract、Package、Copy 怎么选这是 Runnable JAR file 导出对话框里最需要理解的选项对应原帖提到的“第三方包有三种处理方式”。第一种是 Extract required libraries into generated JAR。Eclipse 把第三方 Jar 解压把所有 class 文件混合进生成的 Jar 根部。最终产物是单文件拷走就能跑。缺点是依赖库如果存在同名类后写入的会覆盖先写入的行为上等效于 Class-Path 里后者胜出个别情况下会踩到类冲突。第二种是 Package required libraries into generated JAR。第三方 Jar 保持压缩状态原样塞进生成的 Jar 包内。生成的也是单文件但结构里能看到嵌套的 jar依赖边界清楚一些。第三种是 Copy required libraries into a sub-folder next to the generated JAR。主 Jar 只包含工程自身 class第三方依赖复制到 Jar 同级的 lib 子目录MANIFEST.MF 里写入 Class-Path: lib/xxx.jar。优点是主 Jar 体积小、启动速度快但 jar 和 lib 目录必须一起移动结构一旦破坏就会启动失败。我自己的选择标准和原帖一致默认用第一种。原因是部署时只拷一个文件不容易漏。如果你在乎启动速度或依赖更新频繁第三种也值得用但部署的时候必须把整个目录都拷过去。处理方式生成物运行时依赖目录适合场景Extract单 Jar依赖 class 混入无部署简单不追求结构清晰Package单 Jar内含嵌套 Jar无希望保留依赖边界Copy主 Jar lib 目录必须保持相对位置依赖体积大需要快速启动4.3 Finish 之后验证 Jar 结构jar tf 和 unzip -p 检查导出结束后不要急着拷到目标机器先在本地验证一下jar tf spider.jar | grep -E Main.class|httpclient|log4junzip -p spider.jar META-INF/MANIFEST.MFjar tf 列出 Jar 内部完整文件清单grep 用来确认关键 class 是否存在。选 Extract 方式时能看到 org/apache/http/xxx.class 出现在根下选 Copy 方式时MANIFEST.MF 里会出现 Class-Path: lib/xxx.jar。检查完这两点再去跑运行验证。说明jar 命令在 JDK 的 bin 目录下如果环境只装了 JRE 没有 JDKjar 命令不存在可以用 unzip -l 代替效果一样。5. 运行与排查JVM 参数失效、bat 闪退、依赖缺失的解决记录打包完成只是第一步运行环节才是真正暴露问题的地方。这一章集中记录我在新虚拟机上部署 Jar 时遇到的几类问题每条都按现象、原因、解决来写。5.1 Run Configuration 里的 JVM 参数为什么打包后就不认了原帖作者特别提醒Run Configuration 里设置的 JVM 参数打包完成后不再起作用。很多人绕进过这个误区在 Eclipse 的 Debug 配置里填了 -Xmx1024m以为导出 Jar 时参数会被写进去结果命令行一跑日志显示内存撑爆才意识到参数根本没带上。原因其实很简单Run Configuration 里的 VM arguments 是 Eclipse 调用 java 命令时附加的参数只影响 Eclipse 启动的进程跟工程内容无关。Java 工程的代码和资源可以打进 Jar但 JVM 参数属于启动环境不可能固化在 Jar 内部。标准做法就是运行时在命令行显式指定这也是为什么需要 run.bat。5.2 run.bat 启动脚本一行命令管理 JVM 参数为了让新机器上部署方便我习惯在 Jar 旁边放一个 run.batecho off start java -Xmx256m -Xms128m -jar spider.jarecho off 关闭批处理指令本身的回显start 参数的含义是新开一个控制台窗口运行 java 命令。这里的关键参数是 -Xms128m 初始堆内存和 -Xmx256m 最大堆内存超过 256MB 时 JVM 会抛 OutOfMemoryError。爬虫程序如果并发不高这两个值够用后面要扩大抓取规模直接把 256m 改成 512m 就行不用动代码。注意start 是 Windows 批处理的语法Linux 上没有对应命令。目标机器是 Linux 时用 run.sh#!/bin/bash nohup java -Xmx256m -Xms128m -jar spider.jar run.log 21 nohup 让进程忽略挂断信号退出登录后继续运行 run.log 21把标准输出和错误重定向到日志文件。两条命令的意思一致启动 Jar并让它在后台稳定运行。5.3 踩坑记录四条真实问题翻车现场一NoClassDefFoundError现象新虚拟机上执行 java -jar spider.jar启动几秒后抛 NoClassDefFoundError指向第三方库的某个类。原因导出时选的是 Copy 选项只把 spider.jar 拷贝到了服务器同级的 lib 目录没带过去。解决把 lib 目录和 spider.jar 放到同一层重新执行。记住 Copy 方式下 Jar 和 lib 目录是一对不能拆开。翻车现场二配置文件改了但没生效现象修改了 Jar 同级 conf/spider.properties 里的数据库密码重启后程序依然报旧密码错误。原因代码里用 getResourceAsStream 从 classpath 读配置文件实际在 Jar 内部外部的 conf 文件根本没被读取。解决改成 2.2 节那样用 FileInputStream 读外部配置文件。临时不想改代码可以用 -Dconf.dirconf 重定向但最终还是要从代码层面解决。翻车现场三run.bat 双击后瞬间消失现象双击 bat 文件窗口闪了一下就没了程序没有启动。原因start 新开窗口运行 java如果 java 进程立即异常退出新窗口也随之关闭屏幕上什么都没留下。解决调试时先去掉 start直接在 cmd 里执行 java -Xmx256m -Xms128m -jar spider.jar看到完整错误输出或者改成下面的形式保住窗口echo off java -Xmx256m -Xms128m -jar spider.jar pause翻车现场四日志文件没有生成现象程序在运行但日志目录 logs 下面是空的。原因log4j.properties 里的相对路径 logs/spider.log 是相对于执行 java 命令时所在的目录。如果 cd 到别的目录再执行 java -jar /data/spider/spider.jar日志会写到当前目录下的 logs 里而不是 Jar 旁边的 logs。解决执行前先 cd 到 Jar 所在目录或者在 log4j.properties 里配置绝对路径最稳的做法是在启动脚本里先 cd /data/spider 再运行 java。6. 部署前自检用 jar tf 与一次试跑避免到新机器才翻车打包完成不等于事情结束。我个人的习惯是每次在 Eclipse 里导出 Runnable JAR file 之后强制走一遍自检流程整个下来五分钟左右但能挡掉大部分低级问题。第一步本地试跑。在命令行直接执行 java -jar spider.jar观察日志能不能正常滚出来。这一步的意义是撇开 Eclipse 和 IDE 的调试环境验证 Jar 本身是否真的完整。如果爬虫需要目标 URL 等外部环境可以先做一个 -help 或 -version 参数验证入口类是否正常加载。第二步检查 MANIFEST。用 unzip -p spider.jar META-INF/MANIFEST.MF 看 Main-Class 是否正确如果用了 Copy 方式再确认 Class-Path 里的 lib/*.jar 路径没写错。顺带看一眼目标机器的 Java 环境变量执行 java -version 确认 JDK 版本与本地一致别出现本地 8、线上 17 的版本错位否则部分反射代码直接启动失败。第三步核对目录结构。不管是 Extract 还是 Copy把 Jar 所在的整个目录完整拷到目标机器保持 jar、lib、conf、logs 的相对位置不变。拷完在目标机器上再跑一次 java -jar spider.jar观察前两分钟日志确认没有异常再离开。这里有个小技巧可以用 sha256sum 对比两台机器的 Jar 文件确认传输过程没有损坏sha256sum spider.jar记录本机算出的哈希值到目标机器执行同样的命令对比两个值完全一致说明文件没丢字节。目标机器是 Linux 且用了 run.sh 启动还可以用 jps 确认进程状态jps -l | grep spider.jar进程列表里出现 spider.jar说明 Jar 正在运行。从那以后我每次导出 Runnable JAR file 都会强制走完这三步先跑一次、再看 MANIFEST、最后核对目录相对位置。这套流程看起来笨但确实帮我在新环境里少踩了很多坑。希望帮到你。本文还有配套的精品资源点击获取