为什么 Spring Boot 应用倾向于打 fat jar 直接启动,而早期企业应用倾向于打 war 包从应用容器启动?

Spring Boot选择Fat Jar的本质原因

  1. 与DevOps理念的契合

    • 交付单元标准化

      Fat Jar将应用+容器+依赖封装为单一可执行单元,符合"Build once, deploy anywhere"的DevOps原则

    • 部署流程简化

      消除传统WAR包与容器间的环境耦合,java -jar即可启动,适合CI/CD流水线

    • 版本控制明确

      容器版本与应用版本绑定,避免"运维独立升级容器导致兼容性问题"的经典痛点

  2. 云原生时代的必然选择

    • 不可变基础设施要求

      Fat Jar作为原子构建产物,完美匹配容器镜像的不可变性要求(Dockerfile中只需COPY+ENTRYPOINT)

    • 微服务架构适配

      单个服务独立部署时,轻量级Fat Jar比传统容器更节省资源(如Spring Boot内嵌Tomcat仅30MB左右)

技术演进路线图

graph LR
A[物理机时代] --> B[虚拟机时代] --> C[容器化时代]
A -->|J2EE规范| D[共享应用容器]
D -->|多WAR混部| E[资源利用率优化]
B -->|隔离需求| F[单虚拟机单容器]
C -->|不可变基础设施| G[Fat Jar+容器镜像]
F -->|配置管理复杂| G

关键转折点

  1. J2EE规范的影响(2000s)

    • 设计目标

      通过标准化接口实现"Write once, run anywhere"

    • 副作用

      催生臃肿的应用服务器(WebLogic/WebSphere),部署复杂度反噬开发效率

  2. 虚拟化技术的双重作用(2010s)

    • 积极面

      资源隔离使"单VM单容器"模式可行

    • 遗留问题

      VM镜像仍包含可变状态(如配置文件),违背Immutable原则

  3. Docker的范式革命(2013年后)

    • 镜像分层

      Fat Jar作为应用层,与基础镜像解耦

    • 启动速度

      容器秒级启动 vs 传统容器分钟级启动,使"拆分为微服务"成为可能

行业思潮映射表

时代 核心诉求 技术方案 代表产物
前云计算 硬件资源最大化利用 共享容器+多WAR混部 Tomcat集群
DevOps早期 部署流程自动化 内嵌容器+Fat Jar Spring Boot 1.x
云原生时代 基础设施不可变 容器镜像+声明式配置 Kubernetes

未来演进方向

  1. GraalVM原生镜像

    • 更极端的Fat Jar

      将JVM也打包进去,生成原生二进制

    • 启动时间从秒级降至毫秒级(如Quarkus框架)

  2. Serverless演进

    • Fat Jar转变为Function as a Unit

    • 案例

      AWS Lambda的Java运行时要求zip包含所有依赖

  3. 配置管理的终极形态

    • 从环境变量到ConfigMap,再到Operator模式

    • 最新趋势

      GitOps将配置也纳入版本控制