《Spring Boot 入门》1.1 定位、生态与它解决的问题

本节以一支准备把老系统重构为 Spring Boot 的团队为引子,先用真实的 web.xml 与 applicationContext.xml 展示传统 Spring 的配置负担,再拆解 Spring Boot 的三条核心主张——约定优于配置、起步依赖、内嵌容器,讲清它解决了什么、没解决什么,并梳理 Spring 生态的分工。读完你能判断一个项目该不该上 Boot。

本节目标:搞清楚 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 它解决了什么

把三条主张落到具体收益上:

维度传统 SpringSpring 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 带来了什么 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. 《Spring Boot 入门》18.3 打包与运行
  2. 《Spring Boot 入门》18.2 实现
  3. 《Spring Boot 入门》18.1 需求与设计