Springboot的自动配置究竟如何生效?
从@import讲起
学 Spring Boot 的时候,我一直在想一个问题:明明就加了一个 `spring-boot-starter-web` 的依赖,Tomcat 自己就跑起来了,JSON 解析器也自动好了,我啥也没写。这不科学吧?Java 又不是魔法。直到我扒开 @SpringBootApplication 看了一眼,才发现真相就藏在一行注解里。
拆开 @SpringBootApplication
你要是用 IDE 点进去看,会发现它其实是个套娃:
```java
@SpringBootConfiguration
@EnableAutoConfiguration ← 这个是关键
@ComponentScan
```
@ComponentScan 好理解——扫包。
@SpringBootConfiguration 就是个 @Configuration 的别名。
唯独 @EnableAutoConfiguration 这个名字起得玄乎。"自动配置"四个字听着就让人犯怵,好像 Spring 替你干了多少了不起的事似的。
再点进去:
```java
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { ... }
```
好家伙,又是个 @Import。到这一步就清晰了——所谓"自动配置",本质上就是把一个类 Import 进来干活。
## @Import 到底是什么
很多人把 @Import 当成一个冷门注解,平时用不上。其实它是 Spring 最核心的机制之一。
@Import 做的事很简单:**手动把一个类塞进 IoC 容器**。类似于 @Bean,只不过 @Bean 写在方法上,@Import 写在类上。
Spring 框架里有三种用法:
- **普通类**:`@Import(MyClass.class)` → 直接注册
- **Configuration 类**:`@Import(MyConfig.class)` → 连配置类里的 @Bean 一起注册
- **ImportSelector**:这个最狠——它让你可以动态决定要注册哪些类
自动配置就是第三种。
## AutoConfigurationImportSelector 在干什么
这个 Selector 的套路其实不复杂:
1. 读取 `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` 文件
2. 把里面写的所有类全限定名读出来
3. 用条件注解过滤一遍,把不满足条件的扔掉
4. 剩下的全部注册进容器
说白了,Spring Boot 启动的时候就是去读了一个文件,文件里列了一堆配置类的名字,然后挨个加载。
所以自动配置本身没有任何黑科技。你要是愿意,自己也能写一个 xxx.imports 文件,塞进自己的 jar 包,Spring Boot 启动时就会帮你自动加载。
## 条件注解——为什么加个依赖就生效
现在的问题变成了:既然 AutoConfiguration.imports 里写了那么多配置类,那岂不是不管我用不用,它都会加载?
答案是:**都加载了,但不一定生效。**
每个配置类上面都有一堆条件:
```java
@ConditionalOnClass(RedisOperations.class)
@ConditionalOnMissingBean(RedisTemplate.class)
public class RedisAutoConfiguration { ... }
```
翻译成人话:**"项目里要有 Redis 的依赖,同时用户没自己定义 RedisTemplate,我的配置才生效。"**
这就是为什么你加个 `spring-boot-starter-data-redis`,RedisTemplate 就自动能用了。不是 Spring 猜到了你的心思,而是它加载了 RedisAutoConfiguration,检查类路径下确实有 Redis 的 jar,用户也没自己搞,那就帮你 new 一个。
反过来,你只要自己写了一个 `@Bean RedisTemplate`,Spring 看到 `@ConditionalOnMissingBean`,二话不说就闭嘴了——用户最大。
## 用 OSS Starter 串一遍
这个抽象的概念,我拿自己的阿里云 OSS starter 来串一遍,你就全明白了。
首先我的 autoconfigure 模块里有这几个东西:
**AliyunOSSProperties.java**——负责从 yml 读配置:
```java
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOSSProperties {
private String endpoint;
private String bucketName;
private String region;
// getter/setter
}
```
**AliOssUtil.java**——真正干活的工具类,不戴任何注解:
```java
public class AliOssUtil {
private final AliyunOSSProperties properties;
private OSS ossClient;
public AliOssUtil(AliyunOSSProperties properties) {
this.properties = properties;
}
@PostConstruct
public void init() {
// 用 properties 的值初始化 OSSClient
}
public String upload(MultipartFile file) {
// 上传逻辑
}
}
```
**AliyunOssConfiguration.java**——真正的配置类,负责串联:
```java
@Import(AliyunOSSProperties.class)
@Configuration
public class AliyunOssConfiguration {
@Bean
public AliOssUtil aliOssUtil(AliyunOSSProperties properties) {
return new AliOssUtil(properties);
}
}
```
**AutoConfiguration.imports**——注册文件:
```
com.aliyun.oss.AliyunOssConfiguration
```
启动的时候流程是这样的:
Spring 扫到我的 jar 里的 AutoConfiguration.imports → 看到 AliyunOssConfiguration → 走到 @Import 把 AliyunOSSProperties 塞进容器 → 触发 @ConfigurationProperties 去读 yml 里的 aliyun.oss.* → 把读到的值填进 Properties → 执行 @Bean aliOssUtil,把填好值的 Properties 传进去 → AliOssUtil 拿着配置信息初始化 OSS 客户端。
用户那边只要做两件事:
1. 引入 starter 依赖
2. yml 里写几行配置
然后直接在 Controller 里 @Autowired 就拿到了一个已经配置好的 AliOssUtil。
## 一句话总结
自动配置不是什么魔法。就是 Spring Boot 启动时读了一个文件,按文件里的名单加载配置类,再根据条件注解决定哪些配置实际生效。
整个链路就四个环节:
```
AutoConfiguration.imports → 配置类 → @Import → @Bean
```
搞懂这个,你就弄清楚了 Spring Boot 最引以为傲的"自动配置"到底是个啥。下次面试官问到这个,你就从 @Import 讲起——比你背一百遍"自动配置是 Spring Boot 的一大特性"管用得多。
