第一部分:什麼是 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 支持三種依賴注入方式:

  1. 構造器注入(推薦)
  2. Setter 注入
  3. 字段注入(@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 的三大來源

  1. XML 文件配置
  2. @Bean 方法
  3. @Component 掃描 + 註解(如 @Service、@Repository 等)

3.3 Bean 的完整生命週期(11 個步驟)

  1. BeanDefinition 註冊
  2. 實例化前(InstantiateBefore)
  3. 實例化(new 對象)
  4. 屬性填充(PopulationBean)
  5. 執行 Aware 接口回調(BeanNameAware、BeanFactoryAware、ApplicationContextAware)
  6. BeanPostProcessor 前置處理
  7. @PostConstruct 執行
  8. InitializingBean.afterPropertiesSet()
  9. 自定義 init-method
  10. BeanPostProcessor 後置處理(AOP 代理在這裏創建)
  11. Bean 就緒,可被使用

銷燬階段:

  1. @PreDestroy
  2. DisposableBean.destroy()
  3. 自定義 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;
}

創建過程:

  1. 創建 A → 實例化 A → 把 A 的 ObjectFactory 放入三級緩存
  2. 填充 A 的屬性時需要 B → 開始創建 B
  3. 創建 B → 實例化 B → 把 B 的 ObjectFactory 放入三級緩存
  4. 填充 B 的屬性時需要 A → 從三級緩存拿到 A 的工廠 → 創建早期 A 對象放入二級緩存
  5. B 創建完成
  6. A 繼續填充屬性,拿到完整的 B
  7. A 創建完成

注意:構造器注入的循環依賴無法解決,因為對象還沒創建完就無法放入三級緩存。

第五部分:@ComponentScan 原理深度剖析

5.1 掃描流程

@ComponentScan("com.example")
@Configuration
public class AppConfig {
}

底層調用 ClassPathBeanDefinitionScanner.scan() 方法,核心邏輯:

  1. 查找候選包路徑
  2. 使用 ASM 讀取 class 文件字節碼(不加載類,避免類加載死鎖)
  3. 判斷是否有 @Component 及其衍生註解
  4. 生成 ScannedGenericBeanDefinition
  5. 註冊到 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);
    }
}