从 IDEA 到 java -jar:我第一次完整打通 Java 后端工程链路
文章探讨了Java后端开发中需要手动配置工程体系的原因,指出Android开发之所以简单是因为其工程体系已被IDE和插件封装。作者通过对比普通Java项目和Maven工程,说明Maven解决了软件构建、管理和交付问题。重点解析了mvn package命令的工作流程、jar包不能直接运行的原因,以及maven-jar-plugin配置的本质是生成包含Main-Class的MANIFEST.MF文件。
一、一个很真实的困惑:为什么 Java 这么多要“自己配”?
刚开始用 IDEA 写 Java 后端时,我第一反应其实是懵的:
- 为什么新建 Java 项目没有
pom.xml? - 为什么打出来的 jar 不能直接跑?
- 为什么还要写什么
Main-Class? - 为什么 Android 从来没纠结过这些?
后来一步一步走完这条链路,我才意识到:
Android 不是“不用配”,
而是工程体系已经被 Android Studio + Gradle 插件全部替你配好了。
纯 Java + Maven 更接近“裸 JVM”,工程能力本身就是你要补齐的一层。
二、普通 Java 项目 vs Maven 工程:程序和工程的分水岭
IDEA 默认 Java 项目,本质只是“代码容器”:
src/Main.java
out/
.idea
它解决的是:
👉 代码能不能跑
但 Maven 工程解决的是:
👉 软件如何被构建、管理、交付
当项目里出现:
pom.xmlsrc/main/javasrc/test/javatarget/
这一步,其实已经完成了身份变化:
👉 从“写 Java 程序”
👉 变成“构建软件工程”。
三、mvn package 到底干了什么?
当我第一次执行:
mvn clean package
Maven 做了完整一条工程流水线:
- 编译源码 → class
- 编译测试 → test-class
- 处理资源
- 打包 → jar
- 产物输出到
target/
此时出现的这个文件:
target/myFirstDemo-1.0-SNAPSHOT.jar
在工程意义上非常重要:
👉 这是交付物
👉 不是源码,不是 IDE 项目
👉 是可以被部署、分发、运行的软件实体
这一步,等价于 Android 的:
assembleRelease → 生成 apk/aab
四、为什么 jar 默认不能 java -jar 跑?
我第一次 java -jar xxx.jar 直接报:
没有主清单属性
原因只有一个:
👉 jar 里没有声明入口类
Java 规定:
java -jar 只认 jar 内部一个文件:
META-INF/MANIFEST.MF
里面必须有一行:
Main-Class: com.xxx.Main
否则 JVM 不知道从哪个 main() 开始执行。
五、maven-jar-plugin 那段配置,本质在干嘛?
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.wulong.myfirstdemo.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
这段不是“魔法”。
它只干了一件事:
👉 在打 jar 时,往 META-INF/MANIFEST.MF 里写:
Main-Class: com.wulong.myfirstdemo.Main
结果就是:
👉 jar 从“库文件”升级成“应用程序”
👉 可以独立java -jar启动
六、这一步的 Android 对照非常清晰
| Android | Java 后端 |
|---|---|
| apk / aab | jar |
| assembleRelease | mvn package |
| AndroidManifest.xml | MANIFEST.MF |
| LAUNCHER Activity | Main-Class |
| 点图标启动 | java -jar 启动 |
所以更严谨的类比是:
Main-Class 就是后端 jar 的“启动入口声明”,
和 AndroidManifest 里的 LAUNCHER 本质完全一样。
七、为什么 IDEA 点 ▶ 能跑,但 jar 不能跑?
这是一个非常容易误解的点。
IDEA 点 ▶ 运行:
👉 IDE 帮你指定了入口类
👉 用 classpath 启动 JVM
👉 跟 jar 本身没关系
而 java -jar 是:
👉 完全脱离 IDE
👉 只认 jar 自身携带的信息
👉 所以必须在 MANIFEST 里写入口
这也是:
👉 “能在 IDE 跑” ≠ “能交付”
八、MANIFEST.MF 在哪?我怎么验证?(工程必会)
Main-Class 并不是写在 pom.xml 里就“存在了”,
它真正存在的地方,是在 jar 包内部的一个固定文件中:
META-INF/MANIFEST.MF
这是 JVM 在执行:
java -jar xxx.jar
时唯一会读取的入口说明文件。
1️⃣ MANIFEST.MF 不在工程目录里,它在 jar 里面
你在项目目录中是看不到 MANIFEST.MF 的,因为它是 Maven 在 package 阶段动态生成并打进 jar 的。
它的真实位置永远是:
xxx.jar!/META-INF/MANIFEST.MF
2️⃣ 用一条命令把它“打印出来”(最推荐)
unzip -p /Users/lingpei/Desktop/myFirstDemo-1.0-SNAPSHOT.jar META-INF/MANIFEST.MF
你会看到类似:
Manifest-Version: 1.0
Main-Class: com.wulong.myfirstdemo.Main
这一步非常重要:
👉 这是验证交付物是否具备启动能力
👉 而不是“我感觉 pom 写对了”。
3️⃣ 如果你想“解剖 jar”来看结构
jar tf myFirstDemo-1.0-SNAPSHOT.jar | grep MANIFEST
看到:
META-INF/MANIFEST.MF
说明入口文件确实存在于产物中。
4️⃣ 这一步的工程意义
很多初学者以为:
“我在 pom 里写了 mainClass,所以 jar 能跑。”
但工程上真正严谨的说法是:
👉 Maven 把配置编译进了产物
👉 你必须在产物里验证它存在
这一步非常像 Android 里你去解包 apk,看:
AndroidManifest.xml
是否真的写进安装包。
5️⃣ 一个非常重要的结论
-
pom.xml:工程构建说明书
-
MANIFEST.MF:交付物运行说明书
java -jar 不看 pom,
只看 MANIFEST。
九、我这一轮真正补上的,不是 Maven,而是工程层
这条链路打通后,我对后端工程有了一个非常清晰的底座:
源码 → Maven → 编译 → 打包 → 产物 → 独立运行
这一步解决的从来不是语法,而是:
- 工程交付形态
- 构建系统角色
- 入口与运行模型
- IDE 与产物的边界
也终于理解了:
👉 Android 工程不是“简单”
👉 是工程复杂度被插件体系完全封装了
👉 后端更接近“直面工程本身”。
十、结语
很多人学后端,从 Controller 开始。
但我越来越确认一件事:
架构的起点不是接口,是工程。
而 Maven、jar、MANIFEST、Main-Class 这些看似“配置”的东西,
其实才是后端世界真正的地基。
更多推荐

所有评论(0)