一个 .java 文件从写出到在服务器上跑起来,中间的 compile、build、jar、类加载、Spring Boot 启动
这个文件是纯文本源码,JVM 不认识它。和前端的 .vue/.jsx 不同,Java 需要一个独立的编译步骤把 .java 变成 .class。
javac(Java Compiler)把你写的 .java 源码编译成 字节码(Bytecode),也就是 .class 文件。
你不可能只写一个文件——一个 Spring Boot 项目可能有几十上百个 .java。Maven 负责把它们全部编译、加上依赖库、打成一个可部署的包。
传统 Java Web 项目打成 .war 文件,需要外部 Tomcat 来运行。Spring Boot 改成了 fat jar(胖 jar)——把 Tomcat 本身也打进 jar 包里,所以 java -jar 就能直接启动,不需要额外装 Tomcat。
jar 本质上就是一个 zip 压缩包。你可以用 jar tf 或解压工具看里面的内容。
BOOT-INF/lib/ 里的依赖库加起来可能有 50-100MB。这就是为什么 Spring Boot jar 通常很大——它包含了运行所需的一切。
java -jar 启动时,JVM 会按需把 .class 文件加载到内存里。不是一次性全部加载——用到哪个类才加载哪个。
| 类加载器 | 负责加载什么 | 类比 |
|---|---|---|
| Bootstrap ClassLoader | JRE 核心类(java.lang.*, java.util.*) | 图书馆的"基础藏书"——人人都需要的字典 |
| App ClassLoader | 你的代码 + Maven 依赖 | 你的"私人书架"——你的项目和 jar 依赖 |
| LaunchedURLClassLoader | Spring Boot 特有:从 fat jar 的 BOOT-INF/classes/ 和 lib/ 加载 | "移动图书馆"——从打包好的箱子里取书 |
java -jar 做的第一件事:找到 MANIFEST.MF 里指定的 Main-Class,执行它的 main() 方法。
application.yml 加 logging.level.org.springframework=DEBUG 可以看到更详细的加载过程。
| 对比维度 | 前端(Vue/React) | Java(Spring Boot) |
|---|---|---|
| 源码文件 | .vue / .jsx | .java |
| 编译工具 | Vite/Webpack + Babel/vue-compiler | javac + Maven |
| 编译产物 | .js + .css(浏览器直接执行) | .class 字节码(需要 JVM 翻译成机器码) |
| 打包格式 | dist/ 目录(多个 .js + index.html) | 一个 .jar 文件(zip 格式) |
| 运行环境 | 浏览器(用户的电脑) | JVM(服务器上) |
| 依赖管理 | package.json + npm(node_modules/) | pom.xml + Maven(~/.m2/ 本地仓库) |
| 依赖是否打进产物 | ✅ 打进去(bundle 里包含 vue/react 库代码) | ✅ fat jar 里包含所有依赖(BOOT-INF/lib/) |
| 部署方式 | 把 dist/ 放到 Nginx 静态目录 | java -jar myapp.jar(不需要外部容器) |
| 更新方式 | 替换 dist/ 文件,用户刷新浏览器 | 重启 JVM 进程(或热部署) |
| SourceMap | .js.map(映射回 .vue/.jsx) | 不需要(.jar 里保留 .class,IDE 直接调试 .java) |
| 代码混淆 | 有(压缩 + 变量名变 a,b,c) | 通常不做(后端代码用户看不到) |
因为 Java 是静态编译型语言——必须先编译成字节码,JVM 才能运行。JavaScript 是解释型语言——浏览器直接逐行解释执行(虽然有 JIT,但不需要预编译步骤)。
好处:Java 编译时就能发现类型错误、语法错误(编译报错),不用等运行时才暴露。坏处:每次改代码都要等编译(虽然 IDEA 的增量编译很快)。
可以!工具如 JD-GUI、CFR、Procyon 能把 .class 反编译回接近原始的 .java 代码。所以如果你不给 jar 加密,别人解压你的 jar 就能看到业务逻辑。
要保护代码可以用混淆器(ProGuard)——把类名变量名变成 a/b/c,让反编译出来的代码几乎不可读。但不像前端的压缩那么彻底。
因为 Spring Boot 启动时要做很多事情:创建 IoC 容器、扫描所有类、创建所有 Bean、注入依赖、启动 Tomcat、注册路由。每个 Bean 都要实例化(可能还连数据库、加载配置)。
前端开发时 Vite 不打包,即时编译返回,所以很快。生产构建也要几秒到几十秒(和项目大小有关)。
GraalVM Native Image 可以把 Spring Boot 编译成原生可执行文件,启动时间降到几十毫秒——但编译时间更长(几分钟)。
本质一样——都是构建产物的输出目录。target/ 是 Maven 约定的,dist/ 是前端(Vite/Webpack)约定的。里面放的都是最终可部署的文件。
Maven 的 target/classes/ 是编译后的 .class,target/xxx.jar 是打包后的 jar。前端的 dist/assets/ 是打包后的 JS/CSS。
传统 Java Web 打成 .war 部署到外部 Tomcat。Spring Boot 理念是"约定优于配置" + "自包含"——把 Tomcat 嵌入 jar 里,java -jar 就能启动,不需要运维额外装 Tomcat。
好处:部署简单(一个 jar 走天下)、Docker 友好、微服务拆分方便。坏处:jar 文件较大、多个服务各自有 Tomcat(内存开销稍大)。