Spring IOC 容器 默認注冊 Bean 的 8 條規則
(Spring Framework 6.x 源碼級總結)
閱讀提示:把下面 8 條規則背下來,再讀 Spring 源碼時,你會在任何一行代碼里立刻知道「這個 BeanDefinition 是從哪兒來的」。
1?? 環境掃描:從 啟動類所在包 向下遞歸
@SpringBootApplication
內隱含@ComponentScan
無參數 → 掃描當前包及其子包。- 源碼位置:
ComponentScanAnnotationParser#parse()
→ClassPathBeanDefinitionScanner#doScan()
。
2?? 類級別注解 自動注冊
遇到下列注解即注冊為 singleton Bean:
@Component
(含@Service
、@Repository
、@Controller
等元注解)@Configuration
(特殊:還會生成 CGLIB 代理,見規則 4)@Bean
方法所在類如果被@Configuration
標注,則方法返回值也被注冊。
3?? @Bean 方法 注冊規則
- 作用域:默認
singleton
;可通過@Scope
覆蓋。 - 名字:
- 顯式
value/name
→ 直接使用 - 空 → 方法名首字母小寫
- 顯式
- 重載保護:同名 Bean 后注冊的覆蓋先注冊的(
DefaultListableBeanFactory#registerBeanDefinition
拋異常 → 允許覆蓋開關spring.main.allow-bean-definition-overriding=true
)。
4?? @Configuration
特殊邏輯
- 類本身注冊為 lite
@Component
- 同時注冊
ConfigurationClassPostProcessor
,它會:- 解析
@Bean
方法 - 解析
@Import
(普通類、ImportSelector、ImportBeanDefinitionRegistrar) - 解析
@ComponentScan
- 生成 CGLIB 代理 保證
@Bean
方法內部依賴仍是單例。
- 解析
5?? @Import 的三類導入
類型 | 注冊方式 | 示例 |
---|---|---|
普通類 | 直接注冊 singleton | @Import(MyConfig.class) |
ImportSelector | selectImports() 返回 String[] | @EnableCaching |
ImportBeanDefinitionRegistrar | 手動 registry.registerBeanDefinition() | @EnableAspectJAutoProxy |
6?? SPI 機制:spring.factories
& META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
- Spring Boot 啟動時讀取
spring.factories
→EnableAutoConfiguration
列表 - 每個自動配置類內部再用
@Configuration
+@Bean
注冊大量基礎設施(DataSource、WebMvc、Security…)。
7?? FactoryBean & BeanFactoryPostProcessor
FactoryBean#getObject()
的返回值被注冊,名字為&beanName
(取實際對象去掉&
)。BeanFactoryPostProcessor
(如PropertySourcesPlaceholderConfigurer
)可在注冊階段 修改 或 新增 BeanDefinition。
8?? 默認單例作用域 & 延遲初始化
- 默認作用域:
singleton
- 默認非延遲:容器啟動即實例化;加
@Lazy
則延遲到首次調用getBean()
。 - 單例池:
DefaultSingletonBeanRegistry#singletonObjects
(ConcurrentHashMap)。
一張圖總結
啟動類包掃描 → 類注解(@Component/@Configuration) → @Bean方法↓@Import(三類) → @ComponentScan → spring.factories↓ConfigurationClassPostProcessor → 注冊所有BeanDefinition↓DefaultListableBeanFactory#preInstantiateSingletons → 實例化
背完這 8 條,再讀 Spring 源碼時,你會在任意一行 registerBeanDefinition()
前立刻定位“這是哪條規則觸發”。