第一部分:什麼是 IoC 和 DI?為什麼我們需要它?
1.1 傳統方式的問題
在沒有 IoC 容器之前,我們通常這樣寫代碼:
public class OrderService {
private PaymentService paymentService = new PaymentServiceImpl();
public void createOrder() {
paymentService.pay();
}
}
這種寫法存在幾個致命問題:
- 強耦合:OrderService 直接依賴 PaymentServiceImpl 的具體實現
- 難以測試:單元測試時無法方便地替換成 Mock 對象
- 難以切換實現:如果明天想換成另一個支付服務,需要修改源碼
- 對象生命週期不可控:每次 new 都會創建新實例,無法實現單例
1.2 控制反轉的核心思想
IoC 的核心思想是:誰創建對象?誰管理對象的生命週期?誰負責注入依賴?——由容器來負責,業務代碼只負責使用。
這就像你去餐廳吃飯:
- 傳統方式:自己買菜、洗菜、炒菜、端上桌(自己創建依賴)
- IoC 方式:你只需要點菜,廚房(容器)負責準備好一切,服務員(容器)把菜端到你面前
1.3 依賴注入的幾種方式
Spring 支持三種依賴注入方式:
- 構造器注入(推薦)
- Setter 注入
- 字段注入(@Autowired 直接作用在屬性上,便捷但不推薦)
// 構造器注入(推薦)
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
// Setter 注入
@Service
public class OrderService {
private PaymentService paymentService;
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
// 字段注入(不推薦)
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
}
第二部分:Spring IoC 容器的兩種核心實現
Spring IoC 容器實際上是一個接口體系,最頂層的接口是 BeanFactory,實際開發中最常用的是它的子接口 ApplicationContext。
2.1 BeanFactory vs ApplicationContext
|
特性 |
BeanFactory |
ApplicationContext |
|
懶加載 |
默認懶加載 |
默認立即加載(可配置懶加載) |
|
國際化(i18n) |
不支持 |
支持 |
|
事件發佈 |
不支持 |
支持 |
|
資源訪問 |
不支持 |
支持 |
|
環境抽象(Environment) |
不支持 |
支持 |
|
AOP 支持 |
基本支持 |
完整支持 |
|
常用程度 |
極少直接使用 |
實際開發中幾乎全部使用 |
2.2 常見的 ApplicationContext 實現類
// XML 配置方式(傳統)
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 註解方式(現代主流)
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
// Web 環境
ApplicationContext ctx = new AnnotationConfigWebApplicationContext();
第三部分:Bean 的定義、註冊與實例化全過程
3.1 BeanDefinition —— Bean 的“户口本”
在 Spring 容器中,每一個 Bean 都對應一個 BeanDefinition 對象,它描述了:
- Class 類型
- scope(singleton/prototype)
- 懶加載否
- 依賴哪些其他 Bean
- 初始化方法、銷燬方法
- 是否是 primary、是否允許循環依賴等
public class UserService {
}
// 在容器內部,會有一個對應的 BeanDefinition
BeanDefinition bd = new BeanDefinition();
bd.setBeanClassName("com.example.UserService");
bd.setScope("singleton");
bd.setLazyInit(false);
3.2 BeanDefinition 的三大來源
- XML 文件配置
- @Bean 方法
- @Component 掃描 + 註解(如 @Service、@Repository 等)
3.3 Bean 的完整生命週期(11 個步驟)
- BeanDefinition 註冊
- 實例化前(InstantiateBefore)
- 實例化(new 對象)
- 屬性填充(PopulationBean)
- 執行 Aware 接口回調(BeanNameAware、BeanFactoryAware、ApplicationContextAware)
- BeanPostProcessor 前置處理
- @PostConstruct 執行
- InitializingBean.afterPropertiesSet()
- 自定義 init-method
- BeanPostProcessor 後置處理(AOP 代理在這裏創建)
- Bean 就緒,可被使用
銷燬階段:
- @PreDestroy
- DisposableBean.destroy()
- 自定義 destroy-method
第四部分:核心源碼深度解析
4.1 啓動流程總覽(AnnotationConfigApplicationContext)
public static void main(String[] args) {
// 1. 創建容器
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
// 2. 獲取 Bean
UserService userService = context.getBean(UserService.class);
}
關鍵構造函數:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
// 1. 創建 BeanDefinition 讀取器和掃描器
this();
// 2. 註冊配置類
register(annotatedClasses);
// 3. 真正執行啓動
refresh();
}
4.2 refresh() 方法 —— Spring 容器的 13 步大戲
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 準備刷新
prepareRefresh();
// 2. 創建並加載 BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. BeanFactory 預處理(設置類加載器、表達式解析器等)
prepareBeanFactory(beanFactory);
// 4. 子類擴展點
postProcessBeanFactory(beanFactory);
// 5. 執行 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 註冊 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 7. 初始化消息源(i18n)
initMessageSource();
// 8. 初始化事件廣播器
initApplicationEventMulticaster();
// 9. 子類擴展(Web 環境啓動 Tomcat 等)
onRefresh();
// 10. 註冊監聽器
registerListeners();
// 11. 實例化所有單例 Bean(核心!)
finishBeanFactoryInitialization(beanFactory);
// 12. 發佈容器刷新事件
finishRefresh();
}
}
其中第 11 步 finishBeanFactoryInitialization 是最核心的一步,完成了所有非懶加載單例 Bean 的創建。
4.3 創建 Bean 的三級緩存解決循環依賴
Spring 解決循環依賴的核心是“三級緩存”:
/** 一級緩存:完整 Bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 二級緩存:早期暴露的對象(未完成屬性填充) */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/** 三級緩存:對象工廠,用於創建代理對象 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
經典循環依賴場景:
@Service
public class A {
@Autowired
private B b;
}
@Service
public class B {
@Autowired
private A a;
}
創建過程:
- 創建 A → 實例化 A → 把 A 的 ObjectFactory 放入三級緩存
- 填充 A 的屬性時需要 B → 開始創建 B
- 創建 B → 實例化 B → 把 B 的 ObjectFactory 放入三級緩存
- 填充 B 的屬性時需要 A → 從三級緩存拿到 A 的工廠 → 創建早期 A 對象放入二級緩存
- B 創建完成
- A 繼續填充屬性,拿到完整的 B
- A 創建完成
注意:構造器注入的循環依賴無法解決,因為對象還沒創建完就無法放入三級緩存。
第五部分:@ComponentScan 原理深度剖析
5.1 掃描流程
@ComponentScan("com.example")
@Configuration
public class AppConfig {
}
底層調用 ClassPathBeanDefinitionScanner.scan() 方法,核心邏輯:
- 查找候選包路徑
- 使用 ASM 讀取 class 文件字節碼(不加載類,避免類加載死鎖)
- 判斷是否有 @Component 及其衍生註解
- 生成 ScannedGenericBeanDefinition
- 註冊到 BeanFactory
5.2 自定義類型過濾器
@ComponentScan(
includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserRepository.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Deprecated.class)
)
支持的 FilterType:
- ANNOTATION
- ASSIGNABLE_TYPE
- ASPECTJ
- REGEX
- CUSTOM(實現 TypeFilter 接口)
第六部分:@Bean 與 @Configuration 的秘密
6.1 @Configuration + @Bean 的代理機制
@Configuration
public class MyConfig {
@Bean
public A a() {
b(); // 關鍵!會調用代理後的 b(),確保單例
return new A();
}
@Bean
public B b() {
return new B();
}
}
如果沒有代理機制,上面 a() 方法中調用 b() 會創建兩個 B 實例。
Spring 使用 CGLIB 創建代理,核心是 ConfigurationClassEnhancer,攔截所有 @Bean 方法,確保從容器中獲取已有實例。
6.2 @Configuration(proxyBeanMethods = false) —— Lite 模式
Spring 5.2 引入,在配置類不需要相互調用 @Bean 方法時,可以關閉代理,提升啓動速度。
@Configuration(proxyBeanMethods = false) // Lite 模式,不生成代理
public class MyConfig {
}
第七部分:條件裝配 @Conditional 家族
7.1 @Profile
@Profile("prod")
@Configuration
public class ProdConfig {
}
7.2 @ConditionalOnProperty
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
7.3 @ConditionalOnBean / @ConditionalOnMissingBean
@ConditionalOnMissingBean(DataSource.class)
@Bean
public DataSource dataSource() {
// 只有容器中沒有 DataSource 時才創建
}
7.4 自定義 Conditional
public class OnWindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return System.getProperty("os.name").toLowerCase().contains("windows");
}
}
第八部分:Import 家族的高級用法
8.1 @Import 普通類
@Import(UserService.class)
8.2 @Import(ImportSelector.class)
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{EnableCache.class.getName()};
}
}
8.3 @Import(ImportBeanDefinitionRegistrar.class)
可以手動註冊 BeanDefinition,功能最強大。
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
RootBeanDefinition bd = new RootBeanDefinition(MyService.class);
registry.registerBeanDefinition("myService", bd);
}
}