SpringBoot 为什么知道该创建哪些 Bean?自动配置真正的核心是什么
SpringBoot 为什么知道该创建哪些 Bean?自动配置真正的核心是什么
刚学 SpringBoot 的时候,我一直有个疑问。
项目里明明没有写:
@Bean
public RedisTemplate redisTemplate() {
}
也没有:
new RedisTemplate()
但是代码里却可以直接注入:
@Autowired
private RedisTemplate<String, Object> redisTemplate;
甚至连配置类都不用写。
启动项目就能用。
MyBatis 也是一样。
引入依赖:
mybatis-spring-boot-starter
然后写一个 Mapper:
@Mapper
public interface UserMapper {
}
直接就能访问数据库。
这些年大家已经习惯了 SpringBoot 的这种体验。
但仔细想想。
这里其实有一个很关键的问题。
Spring 为什么知道要创建这些 Bean?
很多人的第一反应是:
SpringBoot 自动创建的。
这句话其实不准确。
因为 SpringBoot 根本不知道 Redis 是什么。
也不知道 MyBatis 是什么。
更不知道你项目里到底需要哪些组件。
假设我自己写一个类:
public class PxpClient {
}
然后打成 Jar 包。
引入项目。
SpringBoot 会自动帮我创建 Bean 吗?
答案显然不会。
这说明一件事。
SpringBoot 不会凭空创建 Bean。
一定有人提前告诉了 SpringBoot:
这里有个配置类。
这里有个 Bean。
请帮我加载。
那么问题来了。
到底是谁告诉 SpringBoot 的?
从启动类开始找答案
项目启动入口:
@SpringBootApplication
public class Application {
}
上一篇我们已经分析过。
@SpringBootApplication
最终会进入:
@EnableAutoConfiguration
而:
@EnableAutoConfiguration
又会导入:
@Import(AutoConfigurationImportSelector.class)
看到这里。
答案已经开始浮出水面。
流程如下:
@SpringBootApplication
↓
@EnableAutoConfiguration
↓
AutoConfigurationImportSelector
SpringBoot 自动配置的核心入口出现了。
AutoConfigurationImportSelector 在干什么?
继续 Debug。
进入:
selectImports()
调用链如下:
selectImports()
↓
getAutoConfigurationEntry()
↓
getCandidateConfigurations()
最终进入:
getCandidateConfigurations()
这里就是 SpringBoot 自动配置的关键。
原来 SpringBoot 根本没有扫描 Jar
刚开始学习 SpringBoot 的时候。
我一直以为:
SpringBoot 会扫描所有 Jar 包。
然后找到自动配置类。
结果 Debug 到这里才发现。
根本不是。
SpringBoot 干的事情非常简单。
它只是读取了一份名单。
SpringBoot 2.x:
META-INF/spring.factories
SpringBoot 3.x:
META-INF/spring/
org.springframework.boot.autoconfigure.AutoConfiguration.imports
打开这个文件。
你会看到类似内容:
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
看到这里。
很多同学第一次都会有一种恍然大悟的感觉。
原来 SpringBoot 根本没有猜。
也没有扫描。
而是:
提前登记
提前备案
按名单加载
流程变成:
引入 Starter
↓
获得自动配置名单
↓
读取配置类
↓
导入 Spring 容器
↓
创建 Bean
RedisTemplate 到底在哪创建?
继续进入:
RedisAutoConfiguration
很快就能看到:
@Bean
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory connectionFactory) {
}
到这里真相大白。
原来:
RedisTemplate
根本不是 SpringBoot 凭空创造的。
而是:
RedisAutoConfiguration
提前写好了创建逻辑。
SpringBoot 做的事情只是:
找到配置类
导入配置类
交给 Spring 创建 Bean
那为什么有些自动配置会生效,有些不会?
看到这里。
新的问题又来了。
既然 SpringBoot 已经找到了:
RedisAutoConfiguration
DataSourceAutoConfiguration
WebMvcAutoConfiguration
这些自动配置类。
是不是都会加载?
答案是:
不会。
很多人觉得:
SpringBoot 最厉害的是自动配置。
其实只说对了一半。
更准确地说应该是:
SpringBoot 最厉害的是有条件地自动配置。
例如 Redis 自动配置类中。
经常能看到类似代码:
@Configuration
@ConditionalOnClass(RedisOperations.class)
public class RedisAutoConfiguration {
}
意思很简单:
只有 Redis 相关类存在,
这个配置类才允许加载。
如果项目根本没有引入 Redis 依赖。
那么:
RedisAutoConfiguration
直接跳过。
再比如:
@Bean
@ConditionalOnMissingBean
public RedisTemplate<Object, Object> redisTemplate() {
}
这里又多了一个条件:
容器中不存在 RedisTemplate,
才创建 RedisTemplate。
如果你自己写了:
@Bean
public RedisTemplate redisTemplate() {
}
SpringBoot 默认配置就不会生效。
看到这里。
很多以前觉得奇怪的问题就解释通了。
为什么引入 Starter 有时候生效?
因为条件满足。
为什么有时候不生效?
因为条件不满足。
为什么自己写的 Bean 能覆盖 SpringBoot 默认配置?
因为:
@ConditionalOnMissingBean
发现容器里已经存在同类型 Bean。
于是放弃创建。
所以 SpringBoot 的完整流程其实是:
引入 Starter
↓
读取 AutoConfiguration.imports
↓
找到自动配置类
↓
检查条件
↓
满足条件
↓
创建 Bean
Starter 的本质到底是什么?
很多人觉得:
Starter 就是依赖包。
其实不准确。
更准确地说:
Starter = 依赖集合 + 自动配置入口
里面不仅有代码。
还有:
自动配置类名单
SpringBoot 正是通过这份名单。
知道应该加载哪些配置。
再通过条件装配。
决定哪些配置最终生效。
总结
为什么引入一个 Starter。
项目里就突然多出了一堆 Bean?
答案其实并不复杂。
Starter 提供:
自动配置类
SpringBoot 负责:
发现自动配置类
条件装配负责:
判断是否允许加载
Spring 负责:
创建 Bean
整个流程如下:
Starter
↓
AutoConfiguration.imports
↓
AutoConfiguration
↓
Conditional
↓
Bean
这就是 SpringBoot 自动配置体系最核心的一条链路。
上一篇:
《一个注解启动整个项目?@SpringBootApplication 到底做了什么?》
下一篇:
《Spring MVC 为什么只写一个注解就能接收请求?》
评论区聊聊:
你有没有遇到过自动配置明明存在,但就是没有生效的情况?当时你是怎么排查出来的?
更多推荐

所有评论(0)