当前位置: 首页 > news >正文

Spring Boot 与传统 Spring:从 WAR 到可执行 JAR,颠覆性的部署哲学

引言:一个常见的困惑

如果你是从传统的 Spring MVC 项目过渡到 Spring Boot 的开发者,很可能会有这样一个疑问:为什么以前我们总是打一个 WAR 包丢到 Tomcat 的 webapps 目录下,而现在 Spring Boot 项目直接打成一个 JAR 包,用 java -jar 命令就能启动一个 Web 应用?

这个看似简单的打包方式变化的背后,是 Spring Boot “约定大于配置”“自包含” 设计理念的深刻体现。本文将深入探讨 Spring Boot 与 Tomcat 的关系,并解析这一变革为我们带来的便利与思考。


一、传统模式:WAR 包与外部 Tomcat 的“租客-酒店”关系

在 Spring Boot 诞生之前,Java Web 应用的部署方式几乎是统一的。

1.1 如何工作?

  1. 开发阶段:我们在项目中编写 web.xml、Spring 配置文件,配置 DispatcherServlet、ContextLoaderListener 等。
  2. 打包阶段:使用 Maven 或 Gradle,将项目及其依赖(除了 Servlet API 这类 provided 依赖)打包成一个 WAR (Web Application Archive) 文件。
  3. 部署阶段:我们需要在服务器上预先安装并启动一个 Tomcat 实例。然后将 WAR 文件复制到 Tomcat 的 webapps 目录下。
  4. 运行阶段:Tomcat 监控到新的 WAR 文件,会解压它,加载其中的类和相关库,并根据 web.xml 的配置来启动应用。

1.2 关系比喻:酒店与租客

  • Tomcat:像一个功能齐全的酒店。它独立运行,管理着基础设施(端口 8080、线程池、JNDI 资源、生命周期管理等)。
  • WAR 应用:像一个租客。它“入住”酒店,享受酒店提供的服务,但必须遵守酒店的规则(如指定的 Servlet 版本)。

1.3 这种模式的优缺点

  • 优点
    • 资源共享:一个 Tomcat 可以部署多个应用,节省资源。
    • 运维统一:运维人员可以集中管理一个 Tomcat 实例,进行监控、调优和打补丁。
  • 缺点
    • 环境依赖性强:应用的行为严重依赖外部 Tomcat 的版本和配置。“在我这儿跑得好好的,怎么到你那儿就不行了?”是常见问题。
    • 部署繁琐:需要先配置环境,再部署应用,步骤较多。
    • 移植性差:难以实现完美的“一次构建,到处运行”。

二、Spring Boot 模式:可执行 JAR 与嵌入式容器的“机甲-驾驶员”关系

Spring Boot 引入了一种全新的思路:为什么不把服务器(Tomcat)和应用打包在一起呢?

2.1 如何工作?

  1. 开发阶段:我们引入 spring-boot-starter-web 依赖。这个 Starter 已经默默地帮我们引入了嵌入式 Tomcat 的依赖。
  2. 打包阶段:Spring Boot 的 Maven/Gradle 插件会将所有依赖(包括嵌入式 Tomcat) 以及项目代码一起打包成一个可执行的、胖乎乎的(Fat)JAR 包。这个 JAR 包内有特殊的结构,知道如何启动自己。
  3. 部署与运行阶段:我们不需要安装 Tomcat。只需要在目标机器上安装 JRE,然后执行一条简单的命令:
    java -jar my-spring-boot-app-0.0.1.jar
    
    这条命令会启动应用内的 main 方法(通常是 SpringApplication.run()),该方法会从内部实例化并启动一个 Tomcat 实例,并将当前应用部署上去。

2.2 关系比喻:机甲与驾驶员

  • 可执行 JAR:是一个配备了机甲的驾驶员
    • 驾驶员:是你的业务逻辑代码。
    • 机甲:就是内嵌的 Tomcat,它为驾驶员提供了强大的战斗(Web 服务)能力。
      两者紧密结合,形成一个独立、完整、可随时投入战斗的个体。

2.3 嵌入式容器的魔法

Spring Boot 的嵌入式支持不仅限于 Tomcat。通过简单的排除和引入依赖,你可以轻松切换容器:

<!-- 默认使用 Tomcat -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><!-- 切换至 Jetty -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId>
</dependency><!-- 切换至 Undertow -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

这种灵活性是传统部署模式难以企及的。

2.4 这种模式的优缺点

  • 优点
    • 开发体验极佳:开箱即用,无需关心服务器配置。
    • 部署极其简单:只需要有 Java 环境,一行命令即可运行。完美契合云原生和容器化(Docker)的理念。
    • 环境一致性:应用在任何地方运行的表现都是一致的,彻底解决了环境差异问题。
    • 高度可定制:通过 application.properties 即可轻松配置容器参数(端口、SSL、连接池等),无需触碰 server.xml
  • 缺点
    • 资源占用:每个应用都自带一个服务器,如果机器上部署了多个应用,可能会有多份 Tomcat 开销。
    • 传统运维不适:传统的运维团队可能需要转变观念,从管理几个大的 Tomcat 实例转变为管理大量独立的应用进程。

三、对比总结:一张图看清差异

特性维度Spring Boot (可执行 JAR)传统 Spring (WAR)
容器关系嵌入式 (Embedded),与应用一体外部 (External),与应用分离
打包格式可执行 Fat JARWAR
启动命令java -jar app.jar$TOMCAT_HOME/bin/startup.sh
环境依赖仅需 JRE需预装匹配版本的 Tomcat/JEE 服务器
容器定制应用配置文件 (application.yml)服务器配置文件 (server.xml)
应用隔离性(进程级别隔离)(共享容器,可能相互影响)
适用架构微服务、云原生、分布式系统传统单体应用、企业级应用服务器环境

四、拓展:Spring Boot 也能打 WAR 包?

值得注意的是,Spring Boot 也提供了打 WAR 包的能力。这通常是为了向旧世界妥协,比如部署到公司强制要求使用的 WebLogic、WebSphere 或现有的 Tomcat 集群。

当你这样做时:

  1. 修改 pom.xml 中的打包方式为 <packaging>war</packaging>
  2. 让主应用类继承 SpringBootServletInitializer,这是为了生成符合 Servlet 标准的引导程序。
  3. 打包后的 WAR 文件仍然会包含内嵌的 Tomcat 库,但在部署到外部应用服务器时,这些库会被忽略,应用会使用外部服务器提供的运行时环境。

这就好比你的机甲驾驶员住进了酒店,虽然自带机甲,但酒店规定不准开机,只能使用酒店统一的装备。


结语:理念的进化

WAR 到可执行 JAR,不仅仅是打包方式的改变,更是开发、部署和运维理念的一次重大进化。它标志着Java应用从需要依赖外部环境的“重量级”应用,向自我满足、独立统一的“轻量级”应用的转变。

这种转变极大地降低了Spring的开发门槛,简化了部署流程,并为微服务架构的兴起奠定了坚实的技术基础。理解这两种模式背后的哲学,能帮助我们更好地理解Spring Boot的设计之美,并在不同的技术选型中做出最合适的决定。

http://www.dtcms.com/a/351655.html

相关文章:

  • MEMS陀螺定向短节与传统陀螺工具的区别?
  • 永磁同步电机无速度算法--传统脉振方波注入法(1)
  • 图片生成视频软件深度评测:浅谈视频音频提取技术
  • Boris FX Samplitude Suite 2025.0.0 音频录制/编辑和母带处理
  • 不增加 GPU,首 Token 延迟下降 50%|LLM 服务负载均衡的新实践
  • 如何基于阿里云OpenSearch LLM搭建智能客服平台
  • ssc37x平台的音频应用demo
  • 160.在 Vue3 中用 OpenLayers 解决国内 OpenStreetMap 地图加载不出来的问题
  • Mamba-HoME:面向3D医学影像分割的层次化专家混合新框架
  • 蓝思科技半年净利超11亿,蓝思成绩单怎么分析?
  • 为什么选择爱普生TG5032CFN温补晶振—可穿戴设备?
  • Pycharm
  • 前端漏洞(上)- JSONHijacking 漏洞
  • LangGraph-2-Demo
  • 基于goofys挂载s3到ubuntu, 没用s3fs因为它兼容性不太好
  • Stream流中的Map与flatMap的区别
  • 《从裸机到 GPU 共享:一步一步在 Kubernetes 上部署 NVIDIA GPU Operator + KAI Scheduler》
  • Python训练营打卡Day44-通道注意力(SE注意力)
  • 数字IC前端设计——前仿篇(VCS,DVE,Verdi)
  • 【Redis 进阶】-----哨兵 Sentinel(重点理解流程和原理)
  • 构建智能提示词工程师:LangGraph 的自动化提示词生成流程
  • 【远程软件VNC】经典远程软件VNC保姆级下载安装图文教程(附安装包)
  • STL——priority_queue的使用(快速入门详细)
  • 【硬件-笔试面试题-60】硬件/电子工程师,笔试面试题-60,(知识点:CAN总线通信原理)
  • 红黑树下探玄机:C++ setmultiset 的幕后之旅
  • Python在语料库建设中的应用:文本收集、数据清理与文件名管理
  • LeetCode刷题记录 | 长度最小的子数组 螺旋矩阵II
  • curl get post
  • GPMall 项目单机部署全流程GPMall:Redis、MariaDB、Kafka、Nginx 一步到位
  • Java微服务AI集成指南:LangChain4j vs SpringAI