用了两年 Spring Boot,@SpringBootApplication 一加就什么都能用。但一直不知道背后到底发生了什么。直到最近面试被深挖,才认真看了一遍源码。
从一个注解开始
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication 其实是个组合注解:
java
@SpringBootConfiguration // 就是 @Configuration,标记配置类
@EnableAutoConfiguration // 核心:开启自动配置
@ComponentScan // 扫描当前包及子包的 @Component
重点在 @EnableAutoConfiguration。
@EnableAutoConfiguration
java
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
它通过 @Import 导入了 AutoConfigurationImportSelector,这个类会去读一个文件:
bash
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
这个文件里列出了所有自动配置类的全限定名,大概长这样:
erlang
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration
...
Spring Boot 启动的时候,把这些配置类全部加载进来。
以 DataSource 为例
java
@AutoConfiguration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) // classpath 下有这些类才生效
@EnableConfigurationProperties(DataSourceProperties.class) // 绑定 application.yml 的配置
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 用户没自己定义才创建
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
逻辑很清晰:
@ConditionalOnClass:classpath 下有DataSource类才激活(你引入了 mysql-connector 就有)@ConditionalOnMissingBean:如果你自己定义了DataSourceBean,就不创建了DataSourceProperties:从application.yml里读spring.datasource.*配置
所以你只要在 yml 里配了数据库信息,Spring Boot 就自动帮你创建 DataSource。不需要写任何 Java 代码。
条件注解家族
| 注解 | 含义 |
|---|---|
@ConditionalOnClass |
classpath 有某个类才生效 |
@ConditionalOnMissingClass |
classpath 没有某个类才生效 |
@ConditionalOnBean |
容器里有某个 Bean 才生效 |
@ConditionalOnMissingBean |
容器里没有某个 Bean 才创建 |
@ConditionalOnProperty |
配置属性满足条件才生效 |
@ConditionalOnWebApplication |
是 Web 应用才生效 |
这些条件注解是自动配置的核心------满足条件就生效,不满足就跳过。所以引入一堆 starter 也不会冲突。
自定义自动配置
你也可以写自己的 starter:
java
// 1. 定义配置属性
@ConfigurationProperties(prefix = "myservice")
public class MyServiceProperties {
private String apiKey;
private int timeout = 5000;
// getter/setter
}
// 2. 写自动配置类
@AutoConfiguration
@ConditionalOnClass(MyClient.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyClient myClient(MyServiceProperties properties) {
return new MyClient(properties.getApiKey(), properties.getTimeout());
}
}
// 3. 注册到 imports 文件
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.MyServiceAutoConfiguration
别人引入你的 starter 依赖后,配一下 myservice.api-key 就能直接用了。
怎么排查自动配置
启动时加参数,看哪些自动配置生效了、哪些被跳过了:
bash
java -jar app.jar --debug
或者在 application.yml 里:
yaml
debug: true
输出里会有 CONDITIONS EVALUATION REPORT,列出每个配置类的匹配/不匹配条件:
sql
Positive matches:
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found required classes
Negative matches:
RedisAutoConfiguration did not match:
- @ConditionalOnClass did not find required class 'redis.clients.jedis.Jedis'
总结
Spring Boot 自动配置的原理就是:@EnableAutoConfiguration → 读取 imports 文件 → 加载所有配置类 → 每个配置类用条件注解判断是否生效。满足条件就自动创建 Bean,不满足就跳过。你只需要引入 starter + 配 yml,剩下的框架帮你搞定。