从源码解析SpringBoot自动配置原理
从源码解析SpringBoot自动配置原理
前言:从”配置地狱”到”开箱即用”
还记得那些年在SSM(Spring + SpringMVC + MyBatis)中挣扎的日子吗?那时候,搭建一个项目的基础环境往往需要半天甚至更长的时间。我们需要配置web.xml,配置Spring容器,配置SpringMVC的DispatcherServlet,配置数据库连接池,配置事务管理器,配置MyBatis的SqlSessionFactory……大量的XML配置充斥在项目中,稍有不慎就会报错,这种状态被称为”配置地狱”(Configuration Hell)。
SpringBoot的出现彻底终结了这一切。它提出了”约定优于配置”(Convention Over Configuration)的理念,通过自动配置(Auto Configuration)机制,让我们在几分钟内就能快速搭建起一个独立运行的、生产级别的Spring应用。你只需要引入一个starter依赖,SpringBoot就会自动为你配置好相关的Bean。
那么,这种”开箱即用”的魔法背后究竟隐藏着怎样的秘密?今天,就让我们戴上源码的眼镜,彻底撕开自动配置的神秘面纱。本文我们将基于 SpringBoot 2.7.x 版本进行剖析。
一、从启动类的@SpringBootApplication说起
任何一个SpringBoot应用的起点,都是一个带有main方法的类,而这个类上通常会标注一个无比关键的注解:@SpringBootApplication。
1 | |
这个@SpringBootApplication注解聚合了所有自动配置的秘密。点进去看:
1 | |
可以清晰地看到,@SpringBootApplication是一个复合注解(组合注解)。它的三个核心”左膀右臂”分别是:
- **
@SpringBootConfiguration**:它实际上就是@Configuration的派生,表示当前类是一个配置类。这意味着你可以直接在启动类中通过@Bean注解向容器注册Bean。 @ComponentScan:启动组件扫描。默认情况下,它会扫描当前启动类所在包及其所有子包下的所有直接带有@Component及其派生注解(如@Service、@Repository、@Controller)的类,并将其注册为Spring的Bean 。@EnableAutoConfiguration:这才是自动配置的核心总开关。它的作用就是开启SpringBoot的自动配置功能,让SpringBoot根据类路径下的依赖(jar包)、配置文件和已有Bean的情况,来猜测你需要配置哪些Bean 。
既然@EnableAutoConfiguration是核心,那我们的分析就从它入手。
二、深入@EnableAutoConfiguration
打开@EnableAutoConfiguration的源码:
1 | |
这个注解又引入了两个核心组件:
- **
@AutoConfigurationPackage**:自动配置包,主要用来注册当前启动类所在的包,供后续使用。 - **
@Import(AutoConfigurationImportSelector.class)**:这是自动配置逻辑真正执行的入口。@Import是Spring框架中用来导入配置类的一个重要注解,而AutoConfigurationImportSelector是一个ImportSelector的实现类,它的selectImports方法会返回一批需要注册到容器中的类的全限定名 。
三、揭秘@AutoConfigurationPackage
先来看第一个小谜题:@AutoConfigurationPackage是做什么的?
源码如下:
1 | |
它通过@Import导入了AutoConfigurationPackages.Registrar。这是一个ImportBeanDefinitionRegistrar接口的实现类,它的作用是在运行时向容器中注册Bean定义 。
打开Registrar的源码:
1 | |
new PackageImport(metadata).getPackageName()这行代码是关键。metadata封装了标注了@AutoConfigurationPackage注解的类的信息。在启动类上,这个类就是MyApplication本身。通过metadata,它获取了MyApplication所在包的包名(例如 com.example.myapp)。
接着调用AutoConfigurationPackages.register方法:
1 | |
逻辑小结:@AutoConfigurationPackage的核心作用就是将主启动类所在的包注册为一个名为org.springframework.boot.autoconfigure.AutoConfigurationPackages的Bean。这个Bean内部维护了一个包名的列表。这样,容器中的其他组件如果需要获取”根包”路径(例如用于JPA实体扫描),就可以通过AutoConfigurationPackages.get()方法来获取 。
四、AutoConfigurationImportSelector的源码解剖
现在,让我们聚焦于自动配置的真正大脑——AutoConfigurationImportSelector。它实现了DeferredImportSelector接口,而DeferredImportSelector继承自ImportSelector。DeferredImportSelector的特殊之处在于,它的执行时机是在所有@Configuration配置类都处理完毕之后,这样可以保证自动配置类的优先级低于用户自定义的配置,确保用户覆盖的有效性 。
4.1 入口方法:selectImports
当Spring容器处理@Import(AutoConfigurationImportSelector.class)注解时,会调用其selectImports方法。
1 | |
4.2 开关检查:isEnabled
这一步检查自动配置功能是否被全局禁用。
1 | |
如果你想彻底关闭自动配置,可以在application.properties中设置:spring.boot.enableautoconfiguration=false 。
4.3 核心逻辑:getAutoConfigurationEntry
这是自动配置最核心的方法,整个流程的精华都浓缩在这里。
1 | |
4.3.1 获取候选配置:getCandidateConfigurations
这个方法负责加载所有可能的自动配置类。
1 | |
SpringFactoriesLoader.loadFactoryNames方法会扫描classpath下所有jar包中的META-INF/spring.factories文件,找到key为org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的所有value值(即自动配置类的全限定名),并将其加载进来 。
在spring-boot-autoconfigure这个jar包中,就包含了数百个自动配置类的定义。例如:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfigurationorg.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
4.3.2 配置过滤:getConfigurationClassFilter().filter()
过滤这一步至关重要。虽然我们加载了上百个自动配置类,但它们并不会全部生效。只有满足了特定条件的才会被实例化,这就是按需装配。
1 | |
常见的AutoConfigurationImportFilter实现有:
- **
OnClassCondition**:检查类路径下是否存在指定的类(对应@ConditionalOnClass和@ConditionalOnMissingClass)。 - **
OnWebApplicationCondition**:检查是否是Web环境(对应@ConditionalOnWebApplication)。 - **
OnBeanCondition**:检查容器中是否存在指定的Bean(对应@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnSingleCandidate)。
正是通过这一层过滤,自动配置类才能展现出智能的一面 。
五、解析RedisAutoConfiguration
光看理论不够过瘾,我们来剖析一个活生生的自动配置类——RedisAutoConfiguration。
1 | |
5.1 条件注解的魔法
- **
@ConditionalOnClass(RedisOperations.class)**:
当你在pom.xml中引入了spring-boot-starter-data-redis依赖后,RedisOperations类就被添加到了classpath中。这个条件通过,RedisAutoConfiguration配置类才可能被加载。如果没有引入相关依赖,这个配置类会被直接跳过 。 @ConditionalOnMissingBean(name = "redisTemplate"):
即使classpath中有Redis的类,RedisAutoConfiguration也不会霸蛮地创建redisTemplateBean。它会先看看容器里有没有你自己定义的redisTemplate。如果你已经定义了一个,它就认为你”有自定义需求”,尊重你的选择,不再创建默认的。这正是”允许覆盖”原则的体现 。- **
@EnableConfigurationProperties(RedisProperties.class)**:
这个注解让RedisProperties这个属性类生效。RedisProperties上标注了@ConfigurationProperties(prefix = "spring.redis"),它会将配置文件中所有以spring.redis开头的属性值,如spring.redis.host、spring.redis.port,绑定到这个类的字段上。然后,RedisAutoConfiguration就可以从注入的RedisProperties对象中读取配置,来创建相应的连接工厂或模板 。
六、如何手写一个Starter
理解源码的最终目的是为了更好地应用和扩展。下面我们简单梳理一下如何自定义一个Starter。
6.1 Starter的命名规范
- 官方Starter:
spring-boot-starter-{模块} - 自定义Starter:
{模块}-spring-boot-starter
6.2 Starter的模块结构
一个完整的Starter通常包含两个模块:
autoconfigure模块:包含自动配置代码和@ConfigurationProperties。starter模块:是一个空jar,仅用于依赖管理,它依赖了autoconfigure模块以及其他必要的依赖。
6.3 关键步骤
编写属性绑定类:创建
XxxProperties类,用于映射配置文件中的属性。编写自动配置类:创建
XxxAutoConfiguration类,使用@ConditionalOnClass、@ConditionalOnMissingBean等注解来控制配置是否生效,并在其中定义@Bean方法创建服务类。同时通过@EnableConfigurationProperties(XxxProperties.class)注入属性。配置spring.factories:
在resources/META-INF/spring.factories文件中添加:properties
1
2org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.config.XxxAutoConfiguration(Spring Boot 2.7+ 新方式):或者创建
resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,直接写入自动配置类的全限定名,一行一个。
七、总结
核心思想:Spring Boot 自动配置通过 @EnableAutoConfiguration 注解开启,利用 AutoConfigurationImportSelector 中的 selectImports 方法,批量加载所有 META-INF/spring.factories 或 AutoConfiguration.imports 文件中注册的配置类。然后,再通过一系列条件注解(@Conditional)进行筛选,最终只将满足条件的配置类(及其定义的 Bean)注入到 Spring 容器中 。
一句话总结:自动配置 = 批量注册配置类 + 条件装配。
常见问题:
- 问:
@SpringBootApplication是什么?
答:是一个组合注解,包含@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。相当于把这三个注解的功能聚合在了一起。- 问:
@EnableAutoConfiguration是如何工作的?
答:它通过@Import(AutoConfigurationImportSelector.class)导入了一个ImportSelector。AutoConfigurationImportSelector会去读取 classpath 下的META-INF/spring.factories或AutoConfiguration.imports文件,获取所有自动配置类的名字,经过排除和条件过滤后,返回给 Spring 容器进行注册。- 问:Spring Boot 如何做到”按需配置”?
答:通过条件注解,如@ConditionalOnClass、@ConditionalOnMissingBean等。自动配置类上标注了这些注解,只有当指定的类存在、Bean 不存在等特定条件满足时,该自动配置类才会生效,从而实现了按需加载。- 问:如何覆盖 Spring Boot 的自动配置?
答:有多种方式,最常用的有两种:1)直接定义自己的 Bean,因为很多自动配置类上都有@ConditionalOnMissingBean,当检测到你的自定义 Bean 后,它就不会创建默认 Bean。2)在配置文件中通过exclude属性排除特定的自动配置类,例如@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})。- 问:如何在自定义 Starter 中读取配置文件?
答:创建一个@ConfigurationProperties注解的属性类来绑定配置,然后在自动配置类上使用@EnableConfigurationProperties来引入该属性类,这样就可以在自动配置类中通过依赖注入获取配置值,并在@Bean方法中使用。
希望这篇从源码角度的深度解析,能帮你彻底掌握 Spring Boot 的自动配置原理。理解它,不仅能让你在日常开发中更加得心应手,更能在遇到奇怪问题时,拥有深入底层定位问题的能力。