Spring Boot 可以分成下面四层学习:

层级 核心目标 学习结果
第一层:会使用 会创建、配置、启动 Spring Boot 项目 能独立开发普通接口
第二层:懂机制 理解启动、自动配置、条件装配 遇到自动配置问题能自己排查
第三层:会生产化 掌握日志、测试、Actuator、优雅停机 应用能稳定部署和运行
第四层:会扩展 自定义自动配置、Starter、源码调试 能封装公司级公共组件

推荐学习顺序:

1、认识 Spring Boot

一、Spring Boot 是什么?

一句话理解:Spring Boot 是一套帮助我们快速创建、配置、启动和运行 Spring 应用的开发框架。

官方对 Spring Boot 的定位是:让开发者能够轻松创建可以直接运行、适合生产环境的 Spring 应用。

注意:Spring Boot ≠ 完全独立的新框架,Spring Boot = 对 Spring 应用开发过程的自动化和简化。它没有抛弃 Spring,而是把 Spring 应用中大量重复、烦琐的配置工作自动完成。

二、Spring Boot 主要解决什么问题?

在没有 Spring Boot 的情况下,创建一个 Spring 应用通常需要手动处理很多事情:

选择各种组件
        ↓
添加对应依赖
        ↓
配置组件
        ↓
配置运行环境
        ↓
配置服务器
        ↓
部署应用
        ↓
启动应用

Spring Boot 将这个过程简化为:

选择需要的功能
        ↓
编写少量配置
        ↓
运行 main 方法
        ↓
应用启动

Spring Boot 最主要解决以下几个问题。

问题Spring Boot 的解决方式
配置内容太多自动配置
不知道应该引入哪些组件Starter 启动器
组件版本容易冲突统一管理组件版本
需要单独安装服务器提供内嵌服务器
部署流程烦琐可以打包成可执行程序
不同环境配置麻烦外部化配置
应用运行状态难以查看Actuator 监控能力

三、Spring Boot 的核心思想

学习 Spring Boot,首先要理解四个核心思想。

1. 约定优于配置

它的核心思想是:框架提前规定一套常用、合理的默认规则。开发者遵守这些规则时,不需要额外写配置;只有不想使用默认规则时,才需要显式配置。也可以简单理解为:按照框架默认的方式做,框架自动帮你处理;偏离默认方式,才需要自己配置。

注意,它并不是:完全不需要配置。而是:减少重复、机械、没有必要的配置。

Spring Boot 为大量配置提供了默认值。

例如,默认配置文件名称:application.properties、application.yml、application.yaml只要按照 Spring Boot 的约定组织项目,它就能够自动识别并完成很多配置。这意味着:

没有特殊需求
    ↓
直接使用默认配置

有特殊需求
    ↓
覆盖默认配置

所以 Spring Boot 不是“不需要配置”,而是常规情况使用默认配置,特殊情况再自定义配置。

约定与配置的关系
二者并不是互相排斥的。正确关系是:

约定提供默认行为
配置覆盖默认行为

执行逻辑可以理解为:

用户有没有明确配置?
        │
        ├── 有 → 使用用户配置
        │
        └── 没有 → 使用框架约定

因此,“约定优于配置”的“优于”不是说:配置没有用。而是说:在能使用合理默认值的情况下,优先通过统一约定减少配置。

约定优于配置不等于“猜测”
框架并不是随便猜测你的想法,而是按照事先确定的规则工作。例如:

banner.txt 是 Banner
application.yml 是主配置文件
static 目录用于静态资源
启动类所在包及其子包是默认扫描范围
8080 是默认服务器端口

这些都是明确的规则。所以更准确地说:约定就是框架提前写好的默认配置规则。你没有亲自写配置,不代表没有配置。实际上,很多配置已经存在于框架内部,只是框架替你提供了默认值。

如果没有“约定优于配置”会怎样?

假设我们开发一个最简单的 Web 项目。如果框架没有默认约定,可能需要手动配置:

服务器使用什么?
服务器端口是多少?
启动服务器的类在哪里?
Controller 放在哪个包?
静态资源放在哪里?
配置文件叫什么?
日志框架使用什么?
JSON 使用什么组件转换?
错误页面放在哪里?
数据库连接池使用哪一种?

即使所有项目的答案都差不多,也要每次重新写一遍。例如:

<server>
    <type>tomcat</type>
    <port>8080</port>
</server>

<json>
    <converter>jackson</converter>
</json>

<static-resource>
    <path>classpath:/static/</path>
</static-resource>

这些配置大多数项目都一样,却必须反复编写。Spring Boot 则提前约定:

  • 默认使用嵌入式 Tomcat
  • 默认端口是 8080
  • 默认使用 Jackson 处理 JSON
  • 默认配置文件叫 application.properties 或 application.yml
  • 默认静态资源放在 static、public 等目录
  • 默认扫描启动类所在包及其子包

因此,开发者通常不需要配置这些内容。

约定大于配置在Spring Boot中的具体体现

Spring Boot 中最典型的例子:默认端口
创建一个 Spring Boot Web 项目,不配置端口,应用默认启动在:8080端口,你不需要写:

server:
  port: 8080

因为 Spring Boot 已经约定:Web 服务器默认端口是 8080。
遵守约定: 配置文件中什么都不写,不配置 server.port,启动后使用:http://localhost:8080访问。
覆盖约定: 假如你不想使用 8080,就显式配置:

server:
  port: 9090

启动地址就变成:http://localhost:9090

这个例子非常完整地体现了约定优于配置:

场景是否需要配置
使用默认端口 8080不需要
使用自定义端口 9090需要

自动配置中的约定优于配置
Spring Boot 最核心的能力之一是:自动配置,它会根据以下信息判断应该配置什么:

  • 你引入了哪些依赖
  • 类路径下有哪些类
  • 你有没有自己创建某些 Bean
  • 配置文件里写了什么
  • 当前是什么类型的应用

例如,引入 Web 启动器:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring Boot 看到相关依赖后,会按照默认约定准备:

Spring MVC
DispatcherServlet
Jackson
嵌入式 Tomcat
参数转换器
异常处理基础设施
静态资源处理器

所以你只需要写:

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}

启动后就能访问:http://localhost:8080/hello
你没有手动创建 Tomcat,也没有手动配置 DispatcherServlet,但项目仍然可以运行。

配置文件名称的约定
Spring Boot 默认会读取:application.properties或者:application.yml。通常放在:src/main/resources/,完整路径例如:src/main/resources/application.yml

遵守约定: 创建src/main/resources/application.yml,写入:

server:
  port: 9090

Spring Boot 会自动读取。你不需要告诉它:配置文件在 resources 目录、配置文件名叫 application.yml。因为这些已经是默认约定。

不遵守约定: 假设你把配置文件命名为:my-config.yml,Spring Boot 默认不会把它当作主配置文件加载。这时必须额外告诉 Spring Boot,请读取 my-config.yml。这就是:遵守约定,不需要配置;改变约定,需要额外配置。

启动类位置的约定
Spring Boot 默认会扫描:启动类所在的包,以及它下面的所有子包。例如项目结构:

com.example.demo
├── DemoApplication.java
├── controller
│   └── UserController.java
├── service
│   └── UserService.java
└── repository
    └── UserRepository.java

启动类:

package com.example.demo;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

这里的启动类位于:com.example.demo,Spring Boot 默认扫描:

com.example.demo
com.example.demo.controller
com.example.demo.service
com.example.demo.repository

所以这些类通常都能被自动发现:

@RestController
public class UserController {
}
@Service
public class UserService {
}
@Repository
public class UserRepository {
}

你不需要手动配置:

@ComponentScan({
    "com.example.demo.controller",
    "com.example.demo.service",
    "com.example.demo.repository"
})

启动类放错位置会怎样?假设项目结构变成:

com.example.app
└── DemoApplication.java

com.example.controller
└── UserController.java

启动类所在包是:com.example.app,默认扫描范围是:com.example.app,但是 UserController 位于:com.example.controller,它不是 com.example.app 的子包,因此可能扫描不到。这时有两个解决办法。

方法一:遵守约定,调整包结构,将启动类放在共同的父包:

com.example
├── DemoApplication.java
├── controller
│   └── UserController.java
└── service
    └── UserService.java

这是更推荐的做法。

方法二:显式配置扫描路径

@SpringBootApplication
@ComponentScan("com.example")
public class DemoApplication {
}

这里再次体现:包结构遵守 Spring Boot 的约定,就不需要配置扫描路径;包结构不遵守约定,就需要自己指定。

静态资源目录的约定
Spring Boot 默认会从以下位置寻找静态资源:

classpath:/static/
classpath:/public/
classpath:/resources/
classpath:/META-INF/resources/

最常见的是:

src/main/resources/static/

例如:

src/main/resources/static/logo.png

启动项目后,可以访问:http://localhost:8080/logo.png,不需要写 Controller:

@GetMapping("/logo.png")
public byte[] getLogo() {
    // 不需要这样做
}

也不需要手动配置静态资源映射,因为 Spring Boot 已经有默认约定。自定义静态资源路径,假如你非要把静态文件放到:src/main/resources/my-files/。那就需要显式配置资源路径,或者自行实现资源映射。因此:static 目录,不是 Java 语法要求,而是 Spring Boot 的目录约定。

自定义 Banner 也是约定优于配置
Spring Boot 的自定义 Banner。Spring Boot 约定启动时自动查找:src/main/resources/banner.txt,只要创建这个文件:src/main/resources/banner.txt,然后写入:

==================================
       用户管理系统启动中
==================================

启动时就会自动显示。你不需要编写:

File bannerFile = new File("banner.txt");
读取文件……
输出文件……

也不需要告诉 Spring Boot:这是 Banner 文件,请在启动时加载它。因为 Spring Boot 已经约定:resources 目录下名为 banner.txt 的文件,就是启动 Banner。如果文件叫:my-banner.txt那就偏离了默认约定,需要显式配置:

spring:
  banner:
    location: classpath:my-banner.txt

对比一下:

文件名是否需要额外配置
banner.txt不需要
my-banner.txt需要

这就是非常典型的“约定优于配置”。

JSON 转换的约定
假设 Controller 返回一个 Java 对象:

@RestController
public class UserController {

    @GetMapping("/user")
    public User getUser() {
        User user = new User();
        user.setId(1L);
        user.setName("张三");
        return user;
    }
}

对象:

public class User {

    private Long id;
    private String name;

    // getter、setter
}

访问接口时,浏览器收到:

{
  "id": 1,
  "name": "张三"
}

为什么 Java 对象自动变成了 JSON?因为 Spring Boot Web 项目默认约定:

  • 接口返回对象时,使用消息转换器处理
  • 类路径中存在 Jackson 时,默认使用 Jackson
  • @RestController 的返回值默认写入响应体

你不需要每个接口都写:

ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
return json;

修改默认行为, 假如日期默认输出不符合要求,可以配置:

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: Asia/Shanghai

也就是说:

  • 普通 JSON 转换使用默认约定
  • 特殊格式再进行配置

嵌入式服务器的约定
以前部署 Java Web 项目,通常需要:

安装 Tomcat
配置 Tomcat
将项目打成 WAR
把 WAR 放入 Tomcat 的 webapps
启动 Tomcat

Spring Boot Web 项目通常直接内置 Tomcat。只要启动:

SpringApplication.run(DemoApplication.class, args);

Tomcat 就会跟着启动。这是因为 Spring Boot 约定:引入 Web 启动器后,如果没有其他特殊设置,就创建嵌入式 Web 服务器。你不需要手动写:

Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.start();

当然,你也可以更换服务器,例如排除 Tomcat,改用 Jetty。但这是特殊需求,所以需要额外配置依赖。

Bean 名称也有约定
例如:

@Service
public class UserService {
}

默认 Bean 名称通常是:userService。也就是:

  • 类名:UserService
  • 默认 Bean 名:userService

你可以这样获取:

Object bean = applicationContext.getBean("userService");

如果你想指定其他名称:

@Service("myUserService")
public class UserService {
}

这时 Bean 名称变成:myUserService
因此:

写法Bean 名
@Service默认按照类名生成
@Service("myUserService")使用指定名称

这同样是:有默认规则时不需要写;想改变默认规则时再配置。

2. 自动配置

自动配置是 Spring Boot 最核心的能力之一。

Spring Boot 的自动配置核心含义是: Spring Boot 在项目启动时,根据当前项目中已有的依赖、配置项、Bean、运行环境等条件,判断项目需要哪些基础组件,并自动把这些组件创建成 Bean 放入 Spring 容器。

Spring Boot 会根据当前应用中存在的组件、配置项和 Bean,判断应该配置哪些功能。官方文档将其描述为:根据应用中已经加入的依赖,尝试自动配置 Spring 应用。可以把它理解成:

Spring Boot 查看当前应用具备哪些条件
                    ↓
判断应该启用哪些配置
                    ↓
自动创建应用需要的对象

例如,当 Spring Boot 发现应用具备某项 Web 功能所需要的条件时,就会自动准备相应的 Web 运行环境。自动配置不是强制配置。例如,你引入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring Boot 发现项目中出现了:

Spring MVC
Servlet API
Tomcat
Jackson

它便推测:这是一个基于 Servlet 的 Web 项目。于是自动配置:

嵌入式 Tomcat
Spring MVC
DispatcherServlet
JSON 消息转换器
静态资源处理器
错误页面处理
参数绑定和类型转换等组件

Spring Boot 官方对自动配置的描述也是:根据加入项目的 jar 依赖,尝试自动配置 Spring 应用;例如引入 Web Starter 后,由于类路径中出现了 Tomcat 和 Spring MVC,Spring Boot 会据此配置 Web 应用。

先理解:自动配置到底“自动”了什么

自动配置最主要的工作是:自动向 Spring 容器中注册一些基础 Bean。 例如以前可能需要你手动写:

@Configuration
public class WebConfig {

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    public DataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }
}

在 Spring Boot 中,很多这类通用 Bean 不需要你自己创建。你只需要:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456

Spring Boot 就会读取这些配置,自动创建对应的 DataSource Bean。因此,“自动配置”并不是自动写你的业务代码,也不是自动生成 Controller、Service,而是:自动完成框架基础设施的配置和 Bean 注册。

Spring Boot 的自动配置通常遵循一个重要原则:

开发者没有自己配置
        ↓
Spring Boot 使用默认配置

开发者已经自己配置
        ↓
Spring Boot 退让,使用开发者配置

这通常被称为:用户配置优先,自动配置退让。

自动配置的入口:@SpringBootApplication

一个标准的 Spring Boot 启动类通常是:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

其中最关键的是:@SpringBootApplication,它本质上组合了三个重要注解:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

也可以近似理解成:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class DemoApplication {
}

Spring Boot 2.7 官方文档说明,@SpringBootApplication 组合了 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan。其中真正开启自动配置机制的是 @EnableAutoConfiguration。

注解作用
@SpringBootConfiguration表示这是 Spring Boot 的主要配置类
@EnableAutoConfiguration开启 Spring Boot 自动配置
@ComponentScan扫描项目中自己编写的组件

需要特别区分:@ComponentScan负责扫描你自己写的:

@Controller
@Service
@Repository
@Component

而:@EnableAutoConfiguration负责加载 Spring Boot 提供的自动配置类。

自动配置的整体运行流程

可以把自动配置过程理解成下面几步。

启动 Spring Boot
      ↓
读取 @SpringBootApplication
      ↓
发现 @EnableAutoConfiguration
      ↓
加载大量候选自动配置类
      ↓
检查每个自动配置类的生效条件
      ↓
条件满足:注册其中的 Bean
条件不满足:跳过
      ↓
完成 Spring 容器初始化

再展开一点:

1. 找到候选自动配置类
2. 判断项目中有没有相关依赖
3. 判断用户有没有自己创建对应 Bean
4. 判断配置文件中的开关和参数
5. 判断是不是 Web 环境
6. 条件全部满足才执行配置

重点在于:Spring Boot 并不是把所有自动配置全部无条件执行,而是先加载候选配置,再通过条件进行筛选。

Spring Boot 本身提供了大量自动配置类,例如:

DataSourceAutoConfiguration
WebMvcAutoConfiguration
JacksonAutoConfiguration
RabbitAutoConfiguration
RedisAutoConfiguration
MailSenderAutoConfiguration
TransactionAutoConfiguration

这些类主要位于:spring-boot-autoconfigure这个 jar 包中。
在 Spring Boot 2.7 中,自动配置候选类可以登记在下面这个文件中:

META-INF/spring/
org.springframework.boot.autoconfigure.AutoConfiguration.imports

文件内容可以近似理解为:

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration

Spring Boot 启动时读取这个名单,知道有哪些自动配置类可以尝试加载。官方文档说明,Spring Boot 2.7 会检查 jar 中的 AutoConfiguration.imports 文件,该文件按行列出候选自动配置类。但是:出现在候选名单中,不代表一定会生效。后面还要判断各种条件。

通过条件注解决定是否生效,自动配置真正的核心是:@Conditional,也就是:符合某些条件,配置才生效。常见的条件注解包括:

条件注解含义
@ConditionalOnClass类路径中存在指定类时生效
@ConditionalOnMissingClass类路径中不存在指定类时生效
@ConditionalOnBean容器中存在指定 Bean 时生效
@ConditionalOnMissingBean容器中不存在指定 Bean 时生效
@ConditionalOnProperty配置项满足条件时生效
@ConditionalOnWebApplication当前是 Web 应用时生效
@ConditionalOnNotWebApplication当前不是 Web 应用时生效
@ConditionalOnResource指定资源文件存在时生效

Spring Boot 官方文档把条件分为类条件、Bean 条件、属性条件、资源条件、Web 应用条件等类别。自动配置类最常使用的就是 @ConditionalOnClass 和 @ConditionalOnMissingBean。

@ConditionalOnClass含义:项目中存在某个类,配置才生效。例如:

@Configuration
@ConditionalOnClass(RabbitTemplate.class)
public class RabbitAutoConfiguration {
}

可以理解为:

项目中存在 RabbitTemplate 类吗?
    │
    ├── 存在 → 说明项目引入了 RabbitMQ 相关依赖
    │          可以继续配置 RabbitMQ
    │
    └── 不存在 → 跳过 RabbitMQ 自动配置

为什么要这样做?因为一个普通 Web 项目可能根本不使用 RabbitMQ。如果没有这个条件,Spring Boot 每次启动都尝试创建 RabbitMQ 组件,就会产生大量错误。

@ConditionalOnMissingBean含义:Spring 容器中不存在指定类型的 Bean 时,才创建默认 Bean。例如下面是简化示意:

@Configuration
public class SomeAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SomeService someService() {
        return new DefaultSomeService();
    }
}

执行逻辑是:

容器中有没有 SomeService?
    │
    ├── 没有 → Spring Boot 创建默认 SomeService
    │
    └── 有   → 使用用户自己的,不再创建默认对象

官方文档也强调,@ConditionalOnMissingBean 的重要作用,就是允许开发者用自己的 Bean 覆盖自动配置提供的默认实现。

@ConditionalOnProperty含义:根据配置文件中的配置项决定是否启用某个功能。例如:

@Configuration
@ConditionalOnProperty(
    prefix = "my.feature",
    name = "enabled",
    havingValue = "true"
)
public class MyFeatureAutoConfiguration {
}

配置:

my:
  feature:
    enabled: true

这时自动配置生效。如果改成:

my:
  feature:
    enabled: false

自动配置不生效。执行逻辑:

my.feature.enabled 是否等于 true?
    │
    ├── 是 → 加载配置
    └── 否 → 跳过配置

@ConditionalOnProperty 可以通过配置属性、期望值以及 matchIfMissing 等参数控制条件。

@ConditionalOnWebApplication含义:只有当前项目是 Web 项目时,配置才生效。例如:

@Configuration
@ConditionalOnWebApplication
public class WebAutoConfiguration {
}

如果当前是:Spring MVC Web 应用就执行。如果只是一个:

普通定时任务程序
批处理程序
命令行程序

则不会执行 Web 相关配置。

把自动配置写成一段伪代码,自动配置可以理解成下面的逻辑:

if (项目中存在某个依赖) {

    if (当前环境符合要求) {

        if (用户没有自己创建对应的Bean) {

            根据application.yml中的配置 {
                创建默认Bean;
                放入Spring容器;
            }
        }
    }
}

数据源自动配置

引入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

再引入 MySQL 驱动:

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>

配置:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

你没有写:

@Bean
public DataSource dataSource() {
    // 手动创建连接池
}

但是仍然可以注入:

@Autowired
private DataSource dataSource;

这是因为 Spring Boot 自动创建了 DataSource Bean。

数据源自动配置大致过程:

项目中存在 DataSource 类吗?
        │
        ├── 否 → 不进行数据源配置
        │
        └── 是
             ↓
容器中已经有 DataSource Bean 吗?
        │
        ├── 有 → 使用用户自己的
        │
        └── 没有
             ↓
读取 spring.datasource 配置
             ↓
选择连接池实现
             ↓
创建 DataSource Bean

在 Spring Boot 2.7 中,如果 HikariCP 存在,默认优先选择 HikariCP;而 spring-boot-starter-jdbc 和 spring-boot-starter-data-jpa 默认会带入 HikariCP。

自动配置通常需要同时满足:

依赖存在
+
环境正确
+
配置合理
+
用户没有提供替代 Bean

这就是 Spring Boot 自动配置最核心的运行模式。

手写一个简单的“自动配置”

下面通过自己实现一个简化版功能,彻底理解自动配置。

假设有一个类:

public class MessageService {

    public String send(String message) {
        return "发送消息:" + message;
    }
}

创建自动配置类:

@Configuration
@ConditionalOnClass(MessageService.class)
public class MessageAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MessageService messageService() {
        return new MessageService();
    }
}

含义是:

如果项目中存在 MessageService 类
并且 Spring 容器中没有 MessageService Bean
就自动创建一个 MessageService

没有用户配置,用户直接注入:

@RestController
public class MessageController {

    private final MessageService messageService;

    public MessageController(MessageService messageService) {
        this.messageService = messageService;
    }
}

Spring Boot 自动配置会创建默认对象,因此可以正常注入。

用户自己提供 Bean

@Configuration
public class MyConfig {

    @Bean
    public MessageService messageService() {
        return new CustomMessageService();
    }
}

此时容器中已经存在 MessageService。所以自动配置中的:@ConditionalOnMissingBean,不满足,默认对象不会创建。

加入配置开关

@Configuration
@ConditionalOnClass(MessageService.class)
@ConditionalOnProperty(
    prefix = "message",
    name = "enabled",
    havingValue = "true",
    matchIfMissing = true
)
public class MessageAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MessageService messageService() {
        return new MessageService();
    }
}

默认不配置时:matchIfMissing = true,所以功能开启。关闭:

message:
  enabled: false

这时整个自动配置不生效。这就是一个完整的简化版自动配置:

类存在
+
配置项允许
+
用户没有自己的 Bean
=
创建默认 Bean

3. Starter 启动器

在 Spring Boot 中,Starter 通常翻译为:启动器,但它并不是“负责启动 Spring Boot 的程序”。更准确地说:Starter 是为某一种开发场景准备好的依赖集合。 例如,你要开发 Web 接口,需要:

  • Spring MVC
  • Spring Web
  • JSON 转换
  • 嵌入式 Tomcat
  • 日志组件

你不需要逐个寻找并引入,只需要添加:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring Boot 官方把 Starter 定义为一种方便的“依赖描述器”:它为某个开发场景集中提供所需的 Spring 及相关技术依赖,开发者不必四处寻找并复制大量依赖配置。一句话记忆:Starter 就是 Spring Boot 提供的“依赖套餐”、“功能集合”。

为什么需要 Starter

假设没有 Starter,你想开发一个 Web 项目,可能需要自己添加很多依赖:

<dependencies>
    <!-- Spring Web -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>

    <!-- Spring MVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>

    <!-- Tomcat -->
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
    </dependency>

    <!-- JSON -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

    <!-- 日志 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
    </dependency>
</dependencies>

这还没有完全列完。你还需要考虑:

这些依赖的版本是否兼容?
有没有漏掉传递依赖?
Tomcat 和 Spring MVC 版本是否匹配?
Jackson 版本是否匹配?
日志组件是否冲突?

有了 Starter,只需要写:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

它会通过 Maven 的传递依赖,把 Web 开发常用依赖一起引入。所以 Starter 主要解决两个问题:

不用逐个寻找依赖
不用自己组合常用依赖
Starter 的本质是什么

从 Maven 的角度看,Starter 本质上就是:一个提前写好依赖关系的 Maven 模块。 它的核心内容通常不是大量 Java 代码,而是一个类似下面的 pom.xml:

<project>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
    </dependencies>
</project>

当然,这是为了理解而写的简化版本。所以当你引入:spring-boot-starter-web,真正发生的是:

你的项目
   ↓
依赖 spring-boot-starter-web
   ↓
starter-web 又依赖其他模块
   ↓
Maven 继续下载这些模块
   ↓
这些 jar 全部进入项目 classpath

以 Spring Boot 2.7 为例,官方说明:

  • spring-boot-starter-web 用于 Spring MVC Web 和 REST 应用,默认使用嵌入式 Tomcat。
  • spring-boot-starter-jdbc 用于 JDBC,并默认搭配 HikariCP。
  • spring-boot-starter-test 用于测试,包含 JUnit Jupiter、Hamcrest、Mockito 等常用测试库。
什么是传递依赖

理解 Starter,必须理解 Maven 的传递依赖。假设:

你的项目依赖 A
A 又依赖 B
B 又依赖 C

那么 Maven 会把:A、B、C都加入你的项目。这就是传递依赖。对应到 Starter:

你的项目
    ↓
spring-boot-starter-web
    ↓
spring-web
spring-webmvc
spring-boot-starter-json
spring-boot-starter-tomcat
spring-boot-starter
    ↓
更多底层依赖

所以你只在 pom.xml 中写了一个 Starter,但 IDEA 的 Maven 依赖列表中会出现很多 jar。这不是 Spring Boot 偷偷改了 pom.xml,而是 Maven 根据依赖关系,自动解析出了所有传递依赖。

例子:spring-boot-starter-web

引入 Starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

这个 Starter 面向的场景是:

开发 Spring MVC Web 项目
开发 RESTful 接口
启动嵌入式 Web 服务器
进行 JSON 数据转换

Spring Boot 2.7 官方文档说明,spring-boot-starter-web 用于构建基于 Spring MVC 的 Web 和 REST 应用,默认嵌入式容器是 Tomcat。

可以简化理解为它大致引入以下内容:

spring-boot-starter-web
│
├── spring-boot-starter
│   ├── Spring Boot 核心组件
│   ├── 自动配置支持
│   ├── 默认日志支持
│   └── YAML 支持
│
├── spring-boot-starter-json
│   └── Jackson 等 JSON 处理组件
│
├── spring-boot-starter-tomcat
│   └── 嵌入式 Tomcat
│
├── spring-web
│   └── Spring 基础 Web 能力
│
└── spring-webmvc
    └── Spring MVC 能力

注意,这是为了学习而简化后的依赖树,不是完整依赖列表。

为什么引入后就能写接口?添加 Starter 后,可以直接写:

@RestController
public class UserController {

    @GetMapping("/user")
    public User getUser() {
        User user = new User();
        user.setId(1L);
        user.setName("张三");
        return user;
    }
}

启动项目后访问:http://localhost:8080/user,得到:

{
  "id": 1,
  "name": "张三"
}

在这个过程中,Starter 为项目准备了所需依赖:Spring MVC、Tomcat、Jackson、Spring Web、日志组件,随后 Spring Boot 自动配置根据这些依赖,自动配置:

嵌入式 Tomcat
DispatcherServlet
Spring MVC
JSON 消息转换器
静态资源处理器

因此,这个过程要拆成两个部分理解:

  • Starter: 把相关依赖引进来
  • 自动配置: 根据这些依赖创建和配置组件

4. 开箱即用

开箱即用,英文是:Out of the Box,可以简单理解为:把东西拿出来后,不需要进行大量安装、组装和配置,就能够直接使用。

在 Spring Boot 中,“开箱即用”通常表示:引入某个 Starter,提供少量必要配置后,Spring Boot 就会自动准备好相关组件,开发者可以直接编写业务代码。

例如开发一个 Web 接口,只需要引入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

然后写一个 Controller:

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello Spring Boot";
    }
}

启动项目后访问:http://localhost:8080/hello,就能得到:Hello Spring Boot。你没有手动:

安装并配置 Tomcat
创建 DispatcherServlet
配置 Spring MVC
配置请求映射处理器
配置字符串响应转换器
指定服务器默认端口

但项目仍然可以运行。这就是 Spring Boot 所说的:开箱即用。

Spring Boot 为什么能做到开箱即用

Spring Boot 的开箱即用,不是某一个单独功能带来的,而是下面几个机制共同实现的:

Starter
+
自动配置
+
约定优于配置
+
内嵌服务器
+
依赖版本管理

完整过程可以理解为:

引入 Starter
      ↓
获得某个场景需要的一组依赖
      ↓
Spring Boot 自动配置检查项目环境
      ↓
按照默认约定创建相关 Bean
      ↓
使用少量配置覆盖必要参数
      ↓
开发者直接编写业务代码
1、Starter:把需要的材料准备好

前面已经学习过 Starter。Starter 的作用是:为某种开发场景准备好常用依赖。例如:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

它会带来 Web 开发常用的依赖,例如:

Spring Web
Spring MVC
Jackson
嵌入式 Tomcat
日志组件

不使用 Starter 时,你可能要一个个寻找和引入这些依赖。使用 Starter 后,只需要引入一个依赖套餐。因此:Starter 负责把材料准备好

2、自动配置:把材料自动组装起来

只有依赖还不够。假设你的项目中有 Tomcat 的 jar,但没有代码创建和启动 Tomcat,它仍然不能自动运行。Spring Boot 的自动配置会根据当前环境判断:

项目中有没有 Tomcat?
项目中有没有 Spring MVC?
当前是不是 Web 项目?
用户有没有自定义服务器配置?
用户有没有自己创建相关 Bean?

条件满足后,Spring Boot 自动创建并配置相关组件。例如:

Starter 引入 Tomcat
        ↓
自动配置发现 Tomcat 类存在
        ↓
创建嵌入式服务器工厂
        ↓
按照默认端口启动 Tomcat

因此:自动配置负责把材料组装起来

3、约定优于配置:提供合理默认值

Spring Boot 为常见场景提供默认值,例如:

服务器默认端口:8080
配置文件默认名称:application.yml
Banner 默认文件名:banner.txt
静态资源默认目录:static
默认 Web 服务器:Tomcat

如果这些默认值符合需求,你不需要配置。例如不写:

server:
  port: 8080

Tomcat 仍然会使用 8080 端口。只有想修改时,才需要配置:

server:
  port: 9090

因此:约定优于配置负责减少需要填写的参数

4、内嵌服务器:不用单独安装 Tomcat

传统 Java Web 项目经常需要:

安装 Tomcat
      ↓
配置 Tomcat
      ↓
项目打成 WAR
      ↓
把 WAR 放入 webapps
      ↓
启动 Tomcat

Spring Boot Web 项目通常使用嵌入式 Tomcat。项目打成 jar 后,可以直接运行:

java -jar demo.jar

Tomcat 会随着 Spring Boot 应用一起启动。所以你不需要:

单独下载 Tomcat
单独配置 Tomcat
把项目复制到 webapps
手动维护外部 Tomcat

这也是 Spring Boot 开箱即用的重要组成部分。

5. 总结:四大核心之间的关系

可以先记住下面这个流程:

Starter
负责把需要的依赖准备好
        ↓
约定优于配置
提供一套默认规则和默认值
        ↓
自动配置
根据依赖、环境、配置自动创建 Bean
        ↓
开箱即用
开发者写少量代码就能运行

也可以这样理解:

核心主要解决的问题
Starter我需要引入哪些依赖?
约定优于配置如果用户没有配置,默认应该怎么做?
自动配置谁来根据环境创建 Tomcat、Spring MVC 等对象?
开箱即用能不能引入依赖后,写少量代码直接运行?

四、Spring Boot 项目的核心启动类

一个典型的 Spring Boot 启动类如下:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

这里有两个重要部分:

  • @SpringBootApplication
  • SpringApplication.run(…)

五、@SpringBootApplication 是什么?

@SpringBootApplication 是 Spring Boot 应用最核心的注解之一。

它不是单一功能注解,而是一个组合注解,主要组合了:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

官方文档也给出了这一对应关系。它们分别代表:

注解主要作用
@SpringBootConfiguration表明当前类是 Spring Boot 配置类
@EnableAutoConfiguration开启 Spring Boot 自动配置
@ComponentScan扫描并注册应用中的组件

因此:@SpringBootApplication,可以大致理解为:

这是一个 Spring Boot 应用
        +
开启自动配置
        +
扫描应用组件

六、为什么启动类通常放在根包?

假设项目结构如下:

com.example
├── Application.java
├── controller
├── service
├── config
└── component

启动类放在:com.example.Application。Spring Boot 默认会从启动类所在包开始,扫描它所在的包以及所有子包。扫描范围可以理解为:

com.example
    ├── controller      √
    ├── service         √
    ├── config          √
    └── component       √

如果某个类位于启动类包结构之外:org.demo.service。默认情况下,它可能不会被扫描到。所以一般建议:将启动类放在项目最外层的根包中。

七、SpringApplication.run() 做了什么?

SpringApplication.run(Application.class, args);

这行代码不是简单地执行一个普通方法。它负责启动整个 Spring Boot 应用。可以先用简化流程理解:

执行 main 方法
        ↓
创建 SpringApplication
        ↓
判断当前应用类型
        ↓
准备运行环境
        ↓
读取配置文件
        ↓
创建 Spring 容器
        ↓
执行自动配置
        ↓
扫描并注册组件
        ↓
创建应用中的对象
        ↓
启动内嵌运行环境
        ↓
执行启动完成后的扩展逻辑
        ↓
应用启动完成

官方文档将 SpringApplication 定义为:一种通过 main() 方法引导启动 Spring 应用的便捷方式。

现阶段不需要立刻研究每一步的源码,但要先记住:SpringApplication.run() 是 Spring Boot 应用的总启动入口。

八、Spring Boot 为什么可以直接运行?

传统 Web 应用经常需要:

准备服务器
    ↓
把应用部署到服务器
    ↓
启动服务器

Spring Boot 可以使用内嵌服务器,把服务器作为应用的一部分运行。 于是启动过程变成:

启动 Spring Boot 应用
        ↓
同时启动内嵌服务器
        ↓
应用开始接收请求

Spring Boot 官方文档说明,Web 应用可以使用内嵌 Tomcat、Jetty 或 Netty 创建自包含的 HTTP 服务器。

因此,一个 Spring Boot 应用通常可以直接通过启动类运行,而不需要先手动把应用部署到外部服务器。

九、Spring Boot 配置文件

Spring Boot 通常使用以下配置文件:
application.properties、application.yml、application.yaml例如application.yml:

server:
  port: 8081

spring:
  application:
    name: demo

配置文件的主要作用是:

修改 Spring Boot 默认配置
配置应用自身参数
配置不同运行环境
控制某些自动配置行为

Spring Boot 支持外部化配置,即同一份应用代码可以在不同环境下使用不同配置;配置来源可以包括配置文件、环境变量和命令行参数等。

十、Spring Boot 的“默认值”和“自定义值”

假设 Spring Boot 默认端口是某个值。应用没有配置端口时:使用 Spring Boot 默认值,应用进行了配置时:

server:
  port: 8088

那么:用户配置覆盖默认配置,因此可以把 Spring Boot 配置体系理解成:

Spring Boot 提供默认配置
             ↓
开发者按需进行覆盖
             ↓
最终形成应用实际配置

学习 Spring Boot 配置时,不只是记住某个属性叫什么,还要理解:

  1. 这个属性控制哪个功能;
  2. 不配置时默认行为是什么;
  3. 配置后改变了什么;
  4. 是否能通过代码方式自定义;
  5. 不同配置来源的优先级如何。

十一、Spring Boot 的主要组成部分

从学习角度,可以把 Spring Boot 分成以下部分:

Spring Boot
├── 启动机制
├── 自动配置
├── Starter
├── 配置文件
├── 配置绑定
├── 环境与 Profile
├── 日志系统
├── Web 应用支持
├── 测试支持
├── 开发工具
├── Actuator
├── 应用打包
└── 应用运行与生产配置

其中最核心的主线是:

启动
  ↓
加载配置
  ↓
创建容器
  ↓
扫描组件
  ↓
执行自动配置
  ↓
创建 Bean
  ↓
启动应用

十二、Spring Boot、Starter、自动配置之间的关系
这是入门阶段最容易混淆的地方。

Starter: 负责准备某个开发场景需要的组件
自动配置: 负责根据当前条件配置这些组件
配置文件: 负责允许开发者修改默认行为
SpringApplication: 负责启动并组织整个应用

它们组合起来:

Starter 准备组件
        ↓
SpringApplication 启动应用
        ↓
自动配置判断条件
        ↓
创建并配置相关对象
        ↓
配置文件覆盖默认值
        ↓
应用运行

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐