SpringBoot自动配置原理解析
SpringBoot自动配置原理解析
前言
SpringBoot最大的特点就是"约定优于配置",让我们无需编写大量的XML配置文件,只需引入相应的starter依赖,就能快速搭建起一个可运行的应用。这背后的核心机制就是自动配置(Auto-Configuration)。
先区分两个容易混淆的概念:
- 自动装配(Auto-Wiring):Spring的依赖注入机制,通过
@Autowired自动注入Bean的依赖 - 自动配置(Auto-Configuration):SpringBoot的特性,自动创建和配置Bean
简单记忆:
- 自动配置:负责"生孩子"(创建Bean)
- 自动装配:负责"送快递"(注入依赖)
本文重点讲解SpringBoot的自动配置原理。
一、什么是自动配置?
1.1 传统Spring的痛点
在传统的Spring应用程序中,开发者需要手动配置大量的bean,例如数据源、事务管理器、视图解析器等。这些配置过程繁琐而容易出错,加大了开发难度和成本。
<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/test"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
bean>
1.2 SpringBoot的自动配置
而在SpringBoot中,只需要:
第一步:在pom.xml中引入starter依赖
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.0version>
dependency>
第二步:在application.yml中配置属性
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
第三步:启动类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
就这么简单! DataSource、TransactionManager、SqlSessionFactory等Bean都会自动创建和配置。
1.3 自动配置的目标
Spring Boot的自动配置实现了以下目标:
- 简化配置:根据应用程序的需求和条件,自动配置所需的组件,避免了繁琐的手动配置过程
- 减少出错:基于条件化配置,避免手动配置过程中出现的错误和疏漏
- 提高效率:提高开发效率,减少开发时间和成本
- 提升可维护性:使得应用程序的配置更为规范和标准化
二、自动配置的三大核心技术
SpringBoot的自动配置主要依靠3个核心的关键技术:
2.1 核心技术一:Starter依赖
什么是Starter?
Starter是SpringBoot提供的一系列依赖描述符,每个Starter依赖都定义了一组常用的依赖库。
Starter的结构:
当你引入 mybatis-spring-boot-starter 时,实际上引入了:
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-autoconfigureartifactId>
dependency>
dependencies>
关键点: 最后一个依赖 mybatis-spring-boot-autoconfigure 就是自动配置的核心!
这个autoconfigure包里包含了:
- 自动配置类:
MybatisAutoConfiguration.java(用@Configuration标注) - 配置属性类:
MybatisProperties.java(用@ConfigurationProperties标注) - spring.factories文件:告诉SpringBoot有哪些自动配置类
2.2 核心技术二:@Configuration配置类 + @Bean
在第三方jar包(autoconfigure包)里,必须要包含一个@Configuration配置类,在这个配置类中,通过@Bean注解声明需要装配到IOC容器里面的Bean对象。
示例:MybatisAutoConfiguration
@Configuration // 标记这是一个配置类
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) // 条件:类路径下有这些类
@ConditionalOnSingleCandidate(DataSource.class) // 条件:容器中有DataSource
@EnableConfigurationProperties(MybatisProperties.class) // 启用配置属性绑定
public class MybatisAutoConfiguration {
// 创建SqlSessionFactory Bean
@Bean
@ConditionalOnMissingBean // 条件:容器中没有SqlSessionFactory才创建
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
return factory.getObject();
}
// 创建SqlSessionTemplate Bean
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
代码解读:
@Configuration:标记这是一个配置类@ConditionalOnClass:只有类路径下有SqlSessionFactory等类时,这个配置才生效@ConditionalOnSingleCandidate(DataSource.class):只有容器中有DataSource时才生效@Bean:声明要创建的Bean@ConditionalOnMissingBean:只有容器中没有这个Bean时才创建(避免覆盖用户自定义的Bean)
2.3 核心技术三:META-INF/spring.factories
什么是spring.factories文件?
这个配置类是放在第三方的jar包里面的,SpringBoot怎么知道它在哪里呢?答案就是:通过SpringBoot中"约定优于配置"的理念,把这个配置类的全路径放在 classpath:/META-INF/spring.factories 文件里面。
文件位置: 在jar包的 META-INF/spring.factories 路径下
如何查看这个文件?
方法一:在IDEA中查看
- 按两次Shift,打开"搜索所有"
- 输入
spring.factories - 选择
spring-boot-autoconfigure包下的文件
方法二:手动查看jar包
- 找到Maven仓库(通常在
C:Users你的用户名.m2 epository) - 找到
org/springframework/boot/spring-boot-autoconfigure/版本号/ - 用压缩软件打开jar包
- 找到
META-INF/spring.factories文件
文件内容示例:
# Auto Configure(自动配置类列表)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,
...(共130+个配置类)
格式说明:
- 键:
org.springframework.boot.autoconfigure.EnableAutoConfiguration - 值:所有自动配置类的全限定名,用逗号和反斜杠分隔
SpringBoot会读取所有jar包中的这个文件,汇总所有的自动配置类。
三、自动配置的执行流程
3.1 整体流程图
1. 应用启动,解析@SpringBootApplication注解
↓
2. 发现@EnableAutoConfiguration注解
↓
3. 加载AutoConfigurationImportSelector类
↓
4. 通过SpringFactoriesLoader读取所有jar包中的META-INF/spring.factories文件
↓
5. 获取所有自动配置类的全限定名
↓
6. 根据@Conditional条件注解过滤(类路径、Bean存在性、配置属性等)
↓
7. 将符合条件的自动配置类加载到Spring容器
↓
8. 自动配置类通过@Bean创建需要的Bean
3.2 核心注解:@SpringBootApplication
@SpringBootConfiguration // 等同于@Configuration
@EnableAutoConfiguration // 核心:启用自动配置
@ComponentScan // 组件扫描
public @interface SpringBootApplication {
}
3.3 核心注解:@EnableAutoConfiguration
@AutoConfigurationPackage // 自动配置包
@Import(AutoConfigurationImportSelector.class) // 核心:导入自动配置选择器
public @interface EnableAutoConfiguration {
// 排除指定的自动配置类
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
重点: @Import(AutoConfigurationImportSelector.class) 导入了自动配置选择器。
3.4 核心类:AutoConfigurationImportSelector
这个类负责选择哪些自动配置类需要加载:
public class AutoConfigurationImportSelector implements DeferredImportSelector {
/**
* 选择需要导入的配置类
* 这个方法会被Spring容器调用
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 1. 检查自动配置是否启用
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 2. 获取自动配置条目
AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(annotationMetadata);
// 3. 返回需要导入的配置类名称数组
return StringUtils.toStringArray(
autoConfigurationEntry.getConfigurations());
}
/**
* 获取自动配置条目
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(
AnnotationMetadata annotationMetadata) {
// 1. 获取@EnableAutoConfiguration注解的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 2. 【核心】获取候选的配置类(从spring.factories文件中读取)
List<String> configurations = getCandidateConfigurations(
annotationMetadata, attributes);
// 3. 去重
configurations = removeDuplicates(configurations);
// 4. 获取需要排除的配置类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 5. 移除排除的配置
configurations.removeAll(exclusions);
// 6. 【核心】过滤配置类(根据@Conditional条件注解)
configurations = getConfigurationClassFilter().filter(configurations);
// 7. 返回最终的配置类列表
return new AutoConfigurationEntry(configurations, exclusions);
}
/**
* 获取候选的配置类
* 通过SpringFactoriesLoader读取spring.factories文件
*/
protected List<String> getCandidateConfigurations(
AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 使用SpringFactoriesLoader加载配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), // EnableAutoConfiguration.class
getBeanClassLoader());
return configurations;
}
}
3.5 SpringFactoriesLoader:读取spring.factories文件
public final class SpringFactoriesLoader {
// spring.factories文件的路径
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
/**
* 加载指定类型的工厂类名称列表
*/
public static List<String> loadFactoryNames(Class<?> factoryType,
ClassLoader classLoader) {
// 1. 获取工厂类型的全限定名(EnableAutoConfiguration)
String factoryTypeName = factoryType.getName();
// 2. 加载所有的spring.factories文件,解析为Map
// 3. 根据factoryTypeName获取对应的配置类列表
return loadSpringFactories(classLoader)
.getOrDefault(factoryTypeName, Collections.emptyList());
}
/**
* 加载所有的spring.factories文件
*/
private static Map<String, List<String>> loadSpringFactories(
ClassLoader classLoader) {
// 1. 先从缓存中获取
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
// 2. 扫描所有jar包中的META-INF/spring.factories文件
Enumeration<URL> urls = classLoader.getResources(
FACTORIES_RESOURCE_LOCATION);
// 3. 遍历所有找到的spring.factories文件
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 4. 加载文件内容为Properties对象
Properties properties = PropertiesLoaderUtils
.loadProperties(resource);
// 5. 解析每一行配置
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray(
(String) entry.getValue());
// 6. 将配置类添加到结果Map中
for (String factoryImplementationName :
factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName,
key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// 7. 去重并缓存结果
result.replaceAll((factoryType, implementations) ->
implementations.stream().distinct()
.collect(Collectors.toList()));
cache.put(classLoader, result);
} catch (IOException ex) {
throw new IllegalArgumentException(
"Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
}
这段代码做了什么?
- 扫描所有jar包中的
META-INF/spring.factories文件 - 读取文件内容,解析为Properties对象
- 根据键(EnableAutoConfiguration)获取所有配置类的全限定名
- 去重并缓存结果
四、条件注解:控制自动配置是否生效
SpringBoot使用条件注解来确定是否需要进行自动配置。常见的条件注解包括:
| 注解 | 说明 | 举例 |
|---|---|---|
| @ConditionalOnClass | 类路径下存在指定类时生效 | 有DataSource类才配置数据源 |
| @ConditionalOnMissingClass | 类路径下不存在指定类时生效 | 没有Redis类就不配置Redis |
| @ConditionalOnBean | 容器中存在指定Bean时生效 | 有DataSource才配置事务管理器 |
| @ConditionalOnMissingBean | 容器中不存在指定Bean时生效 | 用户没配置才用默认配置 |
| @ConditionalOnProperty | 配置文件中存在指定属性时生效 | 配置了redis.host才启用Redis |
| @ConditionalOnResource | 类路径下存在指定资源时生效 | 有某个配置文件才生效 |
| @ConditionalOnWebApplication | 应用是Web应用时生效 | Web环境才配置MVC |
| @ConditionalOnNotWebApplication | 应用不是Web应用时生效 | 非Web环境的配置 |
实际案例:DataSourceAutoConfiguration
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) // 条件1:有数据库相关类
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") // 条件2:没有响应式数据库
@EnableConfigurationProperties(DataSourceProperties.class) // 绑定配置属性
public class DataSourceAutoConfiguration {
@Configuration
@ConditionalOnClass(HikariDataSource.class) // 条件3:有Hikari类
@ConditionalOnMissingBean(DataSource.class) // 条件4:用户没有自己配置DataSource
@ConditionalOnProperty(name = "spring.datasource.type",
havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true) // 条件5:配置了type或没配置(默认Hikari)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(
properties, HikariDataSource.class);
return dataSource;
}
}
}
条件判断逻辑:
- 你引入了数据库依赖吗?(@ConditionalOnClass)
- 你没有用响应式数据库吧?(@ConditionalOnMissingBean)
- 你引入了Hikari依赖吗?(@ConditionalOnClass)
- 你自己配置DataSource了吗?没有我才配置(@ConditionalOnMissingBean)
- 你指定数据源类型了吗?没指定就用Hikari(@ConditionalOnProperty)
只有所有条件都满足,这个配置才会生效!
五、自动配置的优先级
在多个自动配置类中,可能存在相同类型的bean的创建和配置。Spring Boot使用自动配置的优先级来决定哪个自动配置类的设置会生效。
优先级规则:
- 用户自定义配置 > 自动配置:如果你自己配置了Bean,自动配置就不会生效(@ConditionalOnMissingBean)
- 后加载的配置 > 先加载的配置:可以通过@AutoConfigureBefore和@AutoConfigureAfter控制加载顺序
- application.yml配置 > 默认配置:配置文件中的属性会覆盖默认值
示例:自定义配置覆盖自动配置
@Configuration
public class MyConfig {
// 自己配置DataSource,自动配置就不会生效
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
六、如何查看哪些自动配置生效了?
6.1 开启调试模式
在 application.yml 中添加:
debug: true
启动应用后,控制台会输出详细的自动配置报告:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:(生效的配置)
-----------------
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found required classes 'javax.sql.DataSource'
- @ConditionalOnMissingBean types: io.r2dbc.spi.ConnectionFactory (OnBeanCondition)
Negative matches:(未生效的配置)
-----------------
RedisAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.springframework.data.redis.core.RedisOperations'
6.2 排除某个自动配置
方法一:使用exclude属性
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
}
方法二:在配置文件中排除
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
七、总结
7.1 自动配置的三大核心技术
- Starter依赖:引入starter,自动引入相关依赖和autoconfigure包
- @Configuration配置类 + @Bean:在autoconfigure包中定义配置类,通过@Bean创建Bean
- META-INF/spring.factories:通过这个文件告诉SpringBoot有哪些自动配置类
7.2 自动配置的执行流程
- @SpringBootApplication → @EnableAutoConfiguration
- AutoConfigurationImportSelector读取spring.factories
- 获取所有自动配置类
- 根据@Conditional条件注解过滤
- 加载符合条件的配置类
- 配置类通过@Bean创建Bean
7.3 关键概念对比
| 特性 | 自动配置 | 自动装配 |
|---|---|---|
| 所属 | SpringBoot | Spring |
| 作用 | 创建Bean | 注入依赖 |
| 注解 | @EnableAutoConfiguration | @Autowired |
| 时机 | 容器启动时 | Bean实例化时 |
记住: 自动配置负责"生孩子",自动装配负责"送快递"。
八、实战:自定义一个Starter
假设我们要创建一个短信服务的starter。
第一步:创建配置属性类 - 绑定application.yml中的短信配置
// 核心注解:将配置文件中前缀为"sms"的属性,自动绑定到该类的成员变量上
// 比如yml中sms.access-key → 对应本类accessKey(支持短横线转驼峰)
@ConfigurationProperties(prefix = "sms")
public class SmsProperties {
// 短信平台访问密钥(使用者在yml中配置,替换为自己的实际密钥)
private String accessKey;
// 短信平台秘钥(使用者在yml中配置,替换为自己的实际秘钥)
private String secretKey;
// 短信签名(比如【XX公司】,使用者在yml中配置,需短信平台审核通过)
private String signName;
// 必须提供getter/setter:Spring通过反射绑定配置时需要调用,缺一不可
public String getAccessKey() {return accessKey;}
public void setAccessKey(String accessKey) {this.accessKey = accessKey;}
public String getSecretKey() {return secretKey;}
public void setSecretKey(String secretKey) {this.secretKey = secretKey;}
public String getSignName() {return signName;}
public void setSignName(String signName) {this.signName = signName;}
}
第二步:创建服务类 - 封装短信发送业务逻辑
// 短信服务核心类,对外提供短信发送方法,依赖SmsProperties获取配置参数
public class SmsService {
// 注入配置属性类,通过构造器注入(Spring推荐方式,确保属性非空,避免空指针)
private final SmsProperties properties;
// 构造器:接收配置属性对象,由自动配置类传入,无需使用者手动创建
public SmsService(SmsProperties properties) {
this.properties = properties;
}
// 对外提供的短信发送方法,使用者直接调用即可
// 参数说明:phone = 接收短信的手机号;content = 短信内容(需符合短信平台规范)
public void sendSms(String phone, String content) {
// 注意:实际开发中,此处替换为真实短信平台API调用(如阿里云、腾讯云短信)
// 本处仅做模拟发送,打印发送信息,方便新手调试查看
System.out.println("发送短信到: " + phone);
System.out.println("短信内容: " + content);
System.out.println("短信签名: " + properties.getSignName()); // 读取配置中的签名
}
}
第三步:创建自动配置类 - 核心!自动创建Bean并注入Spring容器
// 标记此类为Spring配置类,用于创建Bean、配置Spring容器
@Configuration
// 条件注解:当项目中存在SmsService类时,才会加载本配置类(避免类缺失导致报错)
@ConditionalOnClass(SmsService.class)
// 启用配置属性绑定,将SmsProperties类纳入Spring容器管理,使其可被自动注入
@EnableConfigurationProperties(SmsProperties.class)
public class SmsAutoConfiguration {
// 向Spring容器中注册SmsService类型的Bean,使用者可直接@Autowired注入使用
@Bean
// 条件注解:当容器中没有SmsService类型的Bean时,才创建本Bean
// 核心好处:使用者可自定义SmsService Bean,覆盖默认实现,提升Starter扩展性
@ConditionalOnMissingBean
// 条件注解:配置文件中sms.enabled=true时,才创建Bean;matchIfMissing=true表示未配置enabled时,默认创建
@ConditionalOnProperty(prefix = "sms", name = "enabled", havingValue = "true", matchIfMissing = true)
public SmsService smsService(SmsProperties properties) {
// 传入配置属性对象,创建SmsService实例,交给Spring容器管理
return new SmsService(properties);
}
}
第四步:创建spring.factories文件- 让Spring Boot发现自动配置类
重点:文件路径必须严格一致 → resources/META-INF/spring.factories(路径错了,Spring无法识别自动配置类)
在 resources/META-INF/spring.factories 中:
# 核心配置:指定Spring Boot自动配置的类全路径(固定格式,不可修改前缀)
# 格式:org.springframework.boot.autoconfigure.EnableAutoConfiguration=配置类全限定名
# 末尾表示换行继续(避免单行过长,多个自动配置类用逗号分隔)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.example.autoconfigure.SmsAutoConfiguration
第五步:使用
使用者只需引入Starter依赖(此处省略依赖配置,新手可自行在pom.xml/gradle中引入),再做以下操作即可使用。
# 配置文件(application.yml)- 填写短信相关参数
# 短信服务配置(前缀sms,与配置属性类@ConfigurationProperties(prefix = "sms")一致)
sms:
enabled: true # 是否启用短信服务(可省略,因自动配置中matchIfMissing=true,默认启用)
access-key: your-key # 替换为自己实际的短信平台accessKey
secret-key: your-secret # 替换为自己实际的短信平台secretKey
sign-name: 测试签名 # 替换为自己实际的短信签名(需短信平台审核通过)
//业务代码中使用 - 直接注入,无需手动创建Bean
// 标记为RestController,提供接口测试
@RestController
public class TestController {
// 自动注入Starter中的SmsService Bean(由自动配置类自动创建,无需手动new)
@Autowired
private SmsService smsService;
// 测试接口:启动项目后,访问 http://localhost:8080/send 即可触发短信发送(模拟)
@GetMapping("/send")
public String sendSmsTest() {
// 直接调用SmsService的发送方法,传入手机号和内容即可
smsService.sendSms("13800138000", "验证码:123456(5分钟内有效)");
return "短信发送成功(模拟)";
}
}
写在最后
SpringBoot的自动配置看起来很"神奇",但其实原理很简单:
- 通过starter引入依赖和autoconfigure包
- autoconfigure包里有配置类和spring.factories文件
- SpringBoot启动时读取spring.factories,加载配置类
- 配置类根据条件注解判断是否生效
- 生效的配置类通过@Bean创建需要的Bean
记住这个流程,你就掌握了SpringBoot自动配置的核心原理!
如果这篇文章对你有帮助,请点赞、收藏、关注!
作者:[识君啊]
不要做API的搬运工,要做原理的探索者!
本文地址:https://www.yitenyun.com/5785.html











