网站Logo Ilren 小记

Spring Boot 自动配置原理详解:为什么你什么都没配,系统却能正常运行?

jack
4
2021-07-17

在使用 Spring Boot 开发项目时,你可能会惊讶地发现:只引入几个依赖、写一个启动类,项目就能直接跑起来。甚至数据库、Redis、Web MVC 这些组件都能自动配置好,一切似乎“理所当然”。

但你有没有想过:Spring Boot 到底是怎么做到“自动配置”的?你没配的它帮你配了,你配了的它就乖乖让路?

这篇文章,我们就来彻底搞清楚 Spring Boot 自动配置背后的底层机制、关键注解、执行流程,并告诉你如何自定义自己的自动配置类。

一、什么是 Spring Boot 自动配置?

Spring Boot 的一大核心特性就是自动配置(AutoConfiguration)。它的目标是:

根据你引入的依赖和当前的运行环境,自动创建合适的 Bean 对象并注入 Spring 容器中。

比如你引入了 spring-boot-starter-web,它就自动给你配好 DispatcherServletTomcatWebMvcConfigurer、JSON 序列化等一整套 Web 环境;你再加个 MySQL 依赖,它就自动帮你配置好 DataSource、JdbcTemplate,甚至还连带着事务都给你弄好了。

二、入口注解:@SpringBootApplication@EnableAutoConfiguration

你一定很熟悉这个启动类:

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

@SpringBootApplication 是一个组合注解,等价于:

@Configuration
@ComponentScan
@EnableAutoConfiguration

其中,@EnableAutoConfiguration 才是自动配置的真正入口

它的作用就是开启自动配置功能,让 Spring Boot 去扫描并加载所有符合条件的自动配置类。

三、自动配置类从哪儿来?

Spring Boot 使用了 Java 的 SPI(Service Provider Interface)机制,来加载配置类。具体路径是:

META-INF/spring.factories

你可以在源码中搜索这个文件,例如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
...

也就是说,@EnableAutoConfiguration 会读取这些配置类,把里面的配置都一股脑地加载进 Spring 容器。

但你可能要问:那岂不是所有配置类都加载?那我不用数据库也配个 DataSource 豈不是浪费?

别急,Spring Boot 最聪明的一点在于——它虽然全都加载了,但不会全部生效!

四、智能的关键:条件装配注解 @ConditionalXXX

自动配置类里大量使用了 @Conditional 系列注解。常见的有:

注解

作用

@ConditionalOnClass

某个类存在才配置

@ConditionalOnMissingBean

只有没有你自己配的 Bean 时才自动配置

@ConditionalOnProperty

某个配置项存在或为特定值时才生效

@ConditionalOnWebApplication

Web 项目才生效

@ConditionalOnExpression

SpEL 表达式为 true 才生效

举个例子:DataSourceAutoConfiguration 这个类只有在满足以下条件时才会自动配置数据源:

@Configuration
@ConditionalOnClass({ DataSource.class })
@ConditionalOnProperty(name = "spring.datasource.url")
public class DataSourceAutoConfiguration {
    @Bean
    public DataSource dataSource(...) {
        // 创建 DataSource 实例
    }
}

意思是:只有你的类路径中有 javax.sql.DataSource 且配置了 spring.datasource.url,我才给你自动注入数据库连接池;否则我啥都不做。

所以说,Spring Boot 自动配置虽然“自动”,但其实每一步都经过精密判断。

五、默认配置能不能覆盖?

当然能。Spring Boot 自动配置的另一个贴心设计是:优先级低于用户自定义配置

大部分自动配置类都会加上:

@ConditionalOnMissingBean

也就是说:只要你手动定义了一个同类型的 Bean,自动配置就不会生效了。

比如你自己写了个 DataSource 配置类,Spring Boot 就不会再帮你自动配置数据源了。

六、自动配置的加载流程(源码级简要分析)

自动配置类的加载过程大致可以分为以下几步:

  1. Spring Boot 启动时调用 SpringApplication.run()

  2. 加载并处理 @EnableAutoConfiguration

  3. 使用 SpringFactoriesLoader 读取 spring.factories 中的配置类列表

  4. 利用条件注解(如 @ConditionalOnClass)判断哪些配置生效

  5. 最终通过 ImportSelector 把匹配的配置类注册为 Bean

这其中最关键的类是:

  • AutoConfigurationImportSelector:筛选所有自动配置类

  • SpringFactoriesLoader:从 spring.factories 中读取配置

  • ConditionEvaluator:根据条件判断是否生效

七、如何查看哪些配置生效了?

  1. 开启 debug 模式

application.yml 中添加:

debug: true 

控制台会输出哪些配置类生效了,哪些被忽略了。

  1. 引入 Spring Boot Actuator

加入依赖:

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

然后访问接口 /actuator/conditions,可以看到所有自动配置类的启用/禁用状态。

八、自定义自动配置类

Spring Boot 也允许你写自己的自动配置类并注册到全局。

  1. 创建配置类:

@Configuration
@ConditionalOnClass(MyService.class)
public class MyServiceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyService myService() {
        return new MyService();
    }
}
  1. resources/META-INF/spring.factories 中声明:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfig.MyServiceAutoConfiguration
  1. 只要你的模块被引用,这个自动配置就会生效。

九、总结

问题

回答

自动配置的核心入口是什么?

@EnableAutoConfiguration 注解

自动配置类是如何被发现的?

通过 spring.factories 配合 SPI 机制

为什么配置很智能?

条件注解判断是否加载配置

能否自定义配置或覆盖默认?

可以,优先使用用户配置

如何调试或排查?

开启 debug 日志或使用 actuator 的 /conditions

🔚 写在最后

Spring Boot 的自动配置不仅提升了开发效率,也让项目结构更加清晰、干净。但是理解它的原理,能让你写出更加可控、可调优的项目。

如果你正在开发自己的 starter 模块,或者在多模块项目中拆分配置,掌握这一套机制尤为重要。

如果你觉得本文有帮助,欢迎点赞、收藏或转发给更多 Spring Boot 开发者!

动物装饰