本节目标:搞清楚 Spring Boot 到底解决了什么、没解决什么,以及它在 Spring 生态里的位置,从而能对一个具体项目判断「要不要上 Boot」。
适用版本:Spring Boot 4.1.x(Java 21)
1.1 定位、生态与它解决的问题
假设你所在的团队维护着一套 2016 年上线的订单系统:Spring MVC + Hibernate + 一堆 XML,打成 WAR 丢进外置 Tomcat。现在业务要扩,老板说「用 Spring Boot 重构一版」。作为负责选型的人,你要回答的第一个问题不是「怎么写代码」,而是——Spring Boot 到底替我们解决了什么?值不值得重写?
这一节不给结论,先给证据。
1.1.1 先看一个真实的痛:2016 年的 Spring 配置
下面是一个能跑的传统 Spring MVC 项目里,几乎必备的两份 XML。第一份是 web.xml:
<!-- src/main/webapp/WEB-INF/web.xml -->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
第二份是 applicationContext.xml,负责把数据源、JPA、事务管理器这些「基础设施」一个个手工装配起来:
<!-- src/main/resources/applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.example.order"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.example.order.entity"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="false"/>
</bean>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
这两份文件没有任何一行业务逻辑,但它们必须存在,而且必须写得对,否则应用起不来。
1.1.2 这些配置到底痛在哪
把上面的 XML 换成「痛点清单」,会更清楚:
| 痛点 | 具体表现 | 代价 |
|---|---|---|
| 样板配置冗长 | 数据源、事务、视图解析器每次都要手写 | 新项目起步半天到一天 |
| 版本地狱 | spring-orm、hibernate-core、hibernate-validator、jackson-databind 互相牵制 | 升级一个库,连带调五个 |
| 容器外置 | 要装 Tomcat,改 server.xml,配 context.xml | 开发与生产环境不一致 |
| 打包方式重 | 产出 WAR,靠运维脚本部署到容器 | 本地跑起来门槛高 |
| 无统一约定 | 每个人建的项目结构都不一样 | 换团队就要重新理解 |
这些痛点的共同根源是:Spring Framework 本身是一套「能力」,不是一套「产品」。它给了你依赖注入、AOP、事务这些零件,但怎么把零件装成一辆能开的车,全凭你自己。
1.1.3 Spring Boot 的三条核心主张
Spring Boot 不是重写 Spring,而是在 Spring Framework 之上加了一层「装配层」。它只有三条主张,但每一条都精准打在 1.1.2 的痛点上。
主张一:约定优于配置(Convention over Configuration)。 你只要按约定放代码、写属性,框架就替你把 90% 的配置补上。数据源只需要在 application.yml 里写四行连接信息,其余交给自动配置。
主张二:起步依赖(Starter)。 用「一个 starter 拉一整组协调好版本的依赖」替代手工管理版本。想写 Web,引一个 spring-boot-starter-webmvc,Tomcat、Spring MVC、Jackson 全到位。
主张三:内嵌容器(Embedded Container)。 Tomcat 直接跑在应用进程里,main 方法一执行服务就起来了,不再需要外置容器。
三条主张合起来的效果是:一个能提供 HTTP 接口、连数据库的 Spring 应用,从「半天」缩短到「几分钟」。
1.1.4 约定优于配置:自动配置在做什么
以 1.1.1 那份 applicationContext.xml 为例,换成 Spring Boot 之后,它整个消失了,取而代之的是:
# src/main/resources/application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/orders
username: app
password: secret
jpa:
hibernate:
ddl-auto: validate
show-sql: true
Spring Boot 在启动时扫描 classpath:发现你在用 JPA、发现存在数据源配置、发现 HikariCP 在 classpath 上,于是自动创建 DataSource、EntityManagerFactory、TransactionManager 三个 bean,并把事务注解处理也打开。你没有写一行装配代码。
这里要强调一点,避免后续踩坑:自动配置是「有条件」的,不是「魔法」。它靠 @ConditionalOnClass、@ConditionalOnMissingBean 这类注解判断「该不该出手」。第 4 章会专门拆开这套机制,本节你只需记住它的存在。
1.1.5 起步依赖:把版本地狱关进 BOM
传统项目里,pom.xml 的 <dependencies> 往往长这样:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.39</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.3.39</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.4</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.85</version>
</dependency>
用 Spring Boot 之后,这些全部收敛成一行:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
版本从哪来?来自 spring-boot-starter-parent(或 spring-boot-dependencies 这份 BOM)。BOM 里已经为上百个库钉好了「这一版 Spring Boot 认可的、彼此兼容的版本」。你不再需要为「Jackson 用 2.15 还是 2.17」纠结,BOM 替你决定。
注意:4.x 起 Web MVC 的 starter 名从
spring-boot-starter-web改成了spring-boot-starter-webmvc。改名规则与完整对照表在 1.2 Spring Boot 4 带来了什么 里给全。
1.1.6 内嵌容器:从「装 WAR」到「跑 main」
传统部署流程是:mvn package 产出 WAR → 上传到服务器 → 丢进外置 Tomcat 的 webapps/ → 重启容器。开发时你得在本机装一个 Tomcat,再在 IDE 里配一个 Server 运行项。
Spring Boot 把 Tomcat 当普通依赖引进来(spring-boot-starter-tomcat,内嵌场景下由 Web starter 传递引入),应用自己启动它:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
直接 java -jar order.jar 或 IDE 里 Run 这个 main,服务就在 8080 端口监听。部署形态从「WAR + 容器」变成「一个可执行的 JAR」,这是 Boot 对运维影响最直接的一条。
1.1.7 它解决了什么
把三条主张落到具体收益上:
| 维度 | 传统 Spring | Spring Boot |
|---|---|---|
| 起步成本 | 半天到一天搭骨架 | 几分钟(Spring Initializr 生成即跑) |
| 配置量 | 数百行 XML | 几十行 YAML,其余自动配置 |
| 依赖版本 | 手工对齐 | BOM 统一管理 |
| 运行方式 | 外置容器 + WAR | 内嵌容器 + 可执行 JAR |
| 项目结构 | 团队各自为政 | 官方约定,换项目不陌生 |
| 生态集成 | 逐个手工装配 | 一个 starter 一个能力 |
一句话总结:Spring Boot 把「用 Spring 做工程」这件事里,与业务无关的重复劳动标准化了。
1.1.8 它没解决什么(不要当银弹)
如果 Boot 真是银弹,1.1.2 那份老系统只要「换个依赖」就好了。事实并非如此。下面这些是 Boot 不负责的:
- 业务建模:领域模型怎么切、聚合边界在哪,Boot 一点忙都帮不上。
- 架构决策:单体还是微服务、要不要拆库、同步还是异步,Boot 只是实现工具。
- 性能问题:慢 SQL、N+1 查询、连接池配置不当,Boot 只会忠实地把问题暴露出来。
- 团队规范:分层怎么分、包怎么切、命名怎么定,仍要靠团队自己立规矩。
- 复杂分布式一致性:分布式事务、幂等、最终一致性,那是架构问题,不是框架问题。
换句话说,Boot 降低的是「装配成本」,不是「设计成本」。一个架构混乱的系统,用 Boot 重写只会变成一个「启动更快的混乱系统」。
1.1.9 Spring 生态版图
初学者最容易把「Spring」当成一个东西。它其实是一个家族,各成员分工明确:
| 项目 | 职责 | 一句话理解 |
|---|---|---|
| Spring Framework | 核心容器、IoC、AOP、MVC | 地基,其他一切都建在它上面 |
| Spring Boot | 自动配置、起步依赖、内嵌容器 | 装配层,让 Framework 开箱即用 |
| Spring Data | 统一的数据访问抽象(JPA、Redis、MongoDB) | 少写 DAO,多写接口 |
| Spring Security | 认证与授权 | 登录、权限、OAuth2 都归它 |
| Spring Cloud | 服务发现、配置中心、网关、熔断 | 微服务的「配套设施」 |
| Spring Batch | 批处理作业框架 | 定时跑大批量任务 |
| Spring Integration | 企业集成模式(消息路由、通道) | 复杂消息流编排 |
它们的关系是层层叠加,不是并列竞争:
Spring Framework ← 核心容器与 Web 基础
↑
Spring Boot ← 自动配置 / starter / 内嵌容器
↑
Spring Data / Security / Batch ← 面向具体领域的模块
↑
Spring Cloud ← 面向分布式系统的组合
理解了这张图,你就不会问出「Spring Boot 和 Spring MVC 哪个好」这种问题——它们是不同层的东西。本套书的主线是 Spring Boot,但每一章都会指出它背后用到的是 Framework 或某个领域的哪些能力。
1.1.10 三个常见误解
| 误解 | 事实 |
|---|---|
| 「Spring Boot 是一个新框架,和 Spring 没关系」 | 它是 Spring 生态的装配层,没有 Spring Framework 就没有 Boot。你写的 @Service、@Transactional 全是 Framework 的东西 |
| 「Spring Boot 是脚手架 / 代码生成器」 | Initializr 生成的只是一次性骨架。Boot 真正的价值是运行时的自动配置,与「生成代码」是两回事 |
| 「用了 Boot 就不用懂 Spring 了」 | 恰恰相反。自动配置出错时,你必须懂 IoC、条件装配、Bean 生命周期,否则只能靠猜。这也是本套书要讲原理的原因 |
第一条尤其容易误导人。一个直接的证据:Boot 项目里 90% 的注解(@Component、@Autowired、@RequestMapping、@Transactional)都来自 Spring Framework,Boot 自己新增的注解并不多。
1.1.11 学完本节你应该能回答的问题
- 传统 Spring 项目的配置负担具体来自哪几处?(1.1.1、1.1.2)
- Boot 的三条核心主张分别对应哪条痛点?(1.1.3)
- 「约定优于配置」里,那个「约定」是由谁定义的?(1.1.4)
- 为什么说 Boot 降低的是装配成本而不是设计成本?(1.1.8)
- Spring Framework、Spring Boot、Spring Cloud 三者的层次关系是什么?(1.1.9)
回到开头那支团队:Spring Boot 能替他们消除的是「配置与部署」的重复劳动,但订单系统的领域模型、拆分策略、一致性方案,仍要他们自己设计。 想清楚这一点,选型才不会被「框架很火」带偏。
小结
- 传统 Spring 的痛点在样板 XML、依赖版本、外置容器、无统一约定四处,根源是 Framework 只提供能力、不提供产品。
- Spring Boot 用三条主张回应:约定优于配置(自动配置)、起步依赖(BOM 管理版本)、内嵌容器(可执行 JAR)。
- 它解决的是「用 Spring 做工程」中的装配与部署成本,不解决业务建模、架构决策、性能与团队规范。
- Spring 生态是分层的:Framework 是地基,Boot 是装配层,Data / Security / Cloud 面向具体领域。
- Boot 不是新框架,也不是代码生成器;用好它反而要求你更懂 Spring 原理。
本节建立的是「Boot 是装配层」这个心智模型。下一节我们把这层装配层拆开,看 4.x 这一代到底改了什么、为什么改动这么大。
阅读导航:上一节:目录 · 下一节:1.2 Spring Boot 4 带来了什么 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。