Skip to content

Spring Boot 与自动配置 ​

Spring Boot 的诞生回答了一个核心问题:如何让 Spring 应用从"能跑"到"开箱即用"? 自动配置是回答这个问题的核心机制。本章先讲清为什么需要 Spring Boot,再拆解自动配置的实现原理。Starter、配置体系、内嵌容器、启动流程等其余能力,分别见后续章节。

1. 为什么需要 Spring Boot ​

1.1 传统 Spring 开发的痛点 ​

在 Spring Boot 出现之前,搭建一个 Spring Web 项目需要经历一条漫长而痛苦的路径。让我们先回顾这段"黑暗岁月":

第一步:手写 web.xml

xml
<!-- web.xml - 配置 DispatcherServlet -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/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>

第二步:手写 Spring MVC 配置

xml
<!-- spring-mvc.xml -->
<context:component-scan base-package="com.example.controller"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

第三步:手写数据源配置

xml
<!-- applicationContext.xml -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>

第四步:手动管理 Tomcat

将打好的 WAR 包复制到 Tomcat 的 webapps 目录下,启动 Tomcat,祈祷没有 ClassNotFound 或版本冲突。

1.2 三大痛点的对比 ​

痛点传统 SpringSpring Boot
配置方式大量 XML 配置文件零 XML,纯注解 + 自动配置
依赖管理手动添加每个 JAR,处理版本冲突Starter 一站式引入,版本仲裁
Web 容器外部安装 Tomcat,手动部署 WAR内嵌 Tomcat/Jetty,直接运行 JAR
项目结构需要遵循 Servlet 规范目录约定目录结构,main() 启动
开发效率搭建脚手架可能花费半天Spring Initializr 几秒钟生成

1.3 Spring Boot 的设计哲学 ​

Spring Boot 并非一个全新的框架,而是 Spring 的"脚手架"。它的核心设计原则是:

  1. 约定优于配置(Convention over Configuration):提供合理的默认值,开发者只需覆盖差异部分
  2. 开箱即用(Out of the Box):内嵌容器、自动配置、生产级监控,一行代码不写就能跑
  3. 非侵入式(Unobtrusive):不强制使用特定 API,随时可以回退到原生 Spring
java
// 最简单的 Spring Boot 应用 - 引入 spring-boot-starter-web 后就是一个完整的 Web 服务器
@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

前提是 classpath 上有 spring-boot-starter-web。在这个依赖和默认配置下,运行这段代码,内嵌 Tomcat 就会启动,默认监听 8080 端口。没有 XML,没有 WAR 部署,没有外部 Tomcat。@SpringBootApplication 本身不含 Web 依赖,去掉 starter 就不会有内嵌容器。

2. 自动配置原理 ​

2.1 注解分类全景 ​

开始拆 @SpringBootApplication 之前,先把 Spring Boot 的注解按职责归一次类。这一层分类能帮你回答两个问题:看到一个陌生注解时该往哪一类放,以及配置不生效时该从哪一类开始排查。

类别代表注解职责最容易踩的坑
启动注解@SpringBootApplication标记启动类,同时开启扫描与自动配置启动类没放在最外层包,导致部分组件扫不到
配置注解@Configuration、@Bean、@Import、@PropertySource声明 Bean 与配置来源@Configuration 里的 @Bean 方法互调走 CGLIB 代理,@Component 里则不走
条件注解@Conditional、@ConditionalOnClass、@ConditionalOnMissingBean、@Profile决定 Bean 是否注册条件只在启动时评估一次,改配置不会热生效
属性绑定注解@Value、@ConfigurationProperties、@EnableConfigurationProperties把配置值注入字段@Value 无类型安全,嵌套对象用 @ConfigurationProperties

四类注解承担不同职责,在应用启动的不同阶段协同工作,而非一条固定顺序的流水线。启动注解是入口,配置注解声明"容器里有什么",条件注解决定"这个 Bean 要不要进容器",属性绑定注解解决"Bean 里的值从哪来"。前两类回答"是什么",后两类回答"何时、何值"。

2.2 @SpringBootApplication 拆解 ​

@SpringBootApplication 看起来是一个注解,实际上是三个注解的组合:

java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SpringBootConfiguration      // 标记这是一个配置类(本质是 @Configuration)
@EnableAutoConfiguration      // 核心:启用自动配置
@ComponentScan                // 扫描当前包及子包的组件
public @interface SpringBootApplication { ... }

自动配置的魔法集中在 @EnableAutoConfiguration 上。

2.3 自动配置的加载流程 ​

Spring Boot 2.7 起引入 AutoConfiguration.imports 注册机制,2.x 仍兼容旧的 spring.factories,3.0 起才只用新机制。整个加载流程如下:

txt
启动 SpringApplication
    │
    ▼
加载自动配置候选项(AutoConfiguration.imports,2.7 仍兼容 spring.factories)
    │
    ▼
获取所有候选自动配置类(可能有上百个)
    │
    ▼
逐个检查 @Conditional 条件
    │
    ├── 条件满足 → 注册对应的 Bean
    │
    └── 条件不满足 → 跳过,不注册

这段流程背后的执行者是 AutoConfigurationImportSelector。它实现了 DeferredImportSelector,getAutoConfigurationEntry() 负责加载候选自动配置类,完成去重、排除和基于 classpath 元数据的预过滤,随后由 AutoConfigurationGroup 排序,配置类解析阶段再评估完整条件。这样「先解析的配置类所注册的 BeanDefinition」能成为「后解析配置类」条件判断的依据。注意 @ConditionalOnMissingBean 判断的是容器里已注册的 BeanDefinition,不要求对应的 Bean 已经实例化。

以 DataSourceAutoConfiguration 为例,看看条件注解如何工作:

java
@AutoConfiguration(before = SqlInitializationAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })   // classpath 上有 DataSource 与嵌入式数据库枚举才生效
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")      // 容器中不存在 R2DBC 的 ConnectionFactory Bean 才生效
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @Conditional(EmbeddedDatabaseCondition.class)                       // 未设置 spring.datasource.url、无可用连接池,且 classpath 存在嵌入式数据库
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) // 用户没有自定义 DataSource / XADataSource
    @Import(EmbeddedDataSourceConfiguration.class)
    protected static class EmbeddedDatabaseConfiguration {
        // 自动配置嵌入式数据库(H2/HSQL/Derby)
    }

    @Configuration(proxyBeanMethods = false)
    @Conditional(PooledDataSourceCondition.class)                       // 显式配置了 spring.datasource.type,或 classpath 上有 Hikari 等连接池
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    protected static class PooledDataSourceConfiguration {
        // 自动配置连接池(默认 HikariCP)
    }
}

2.4 条件注解家族 ​

Spring Boot 提供了一整套 @Conditional 注解,构成自动配置的"开关系统":

注解作用典型场景
@ConditionalOnClassclasspath 存在指定类引入了 Jackson 才配置 JSON 序列化
@ConditionalOnMissingBean容器中没有指定 Bean用户未自定义数据源才用默认的
@ConditionalOnProperty配置属性满足条件配置了 app.feature.enabled=true 才启用功能
@ConditionalOnWebApplication当前是 Web 应用MVC 配置只在 Web 环境生效
@ConditionalOnResourceclasspath 存在指定资源有 logback.xml 才使用自定义日志
@ConditionalOnExpressionSpEL 表达式为 true复杂条件组合

2.5 自动配置的核心流程图 ​

关键理解:自动配置是"兜底"而非"强制"。当开发者自己注册了同类型的 Bean 时,@ConditionalOnMissingBean 确保自动配置会"让路"。这就是"用户定义优先"原则。

3. 自动配置类的排序 ​

自动配置类不是随便注册的,它们之间常有先后依赖。比如 MyBatis Starter 的 MybatisAutoConfiguration 声明在 DataSourceAutoConfiguration 之后处理,这样它的条件判断能考虑数据源自动配置注册的 BeanDefinition。Spring Boot 用三个注解表达这种顺序:

注解作用示例
@AutoConfigureOrder显式指定一个整数值,值越小越先装配@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfigureBefore声明本类必须在某类之前装配@AutoConfiguration(before = XxxAutoConfiguration.class)
@AutoConfigureAfter声明本类必须在某类之后装配@AutoConfiguration(after = DataSourceAutoConfiguration.class)

@AutoConfiguration 注解本身带 before / after 属性,等价于独立的 @AutoConfigureBefore / @AutoConfigureAfter。排序发生在配置类解析阶段的条件评估之前——顺序确定后,配置类逐个解析,条件才逐个判断,这保证了「前一个配置类注册了哪些 BeanDefinition」这个事实,能被后一个配置类的条件感知到。判断针对的是 BeanDefinition,而非已实例化的 Bean。

4. 本章知识地图 ​

自动配置是 Spring Boot 的发动机,但不是孤立的。围绕它还有六个话题,各成一章:

Released under the MIT License.