首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring IOC容器原理与源码深度剖析

Spring IOC容器原理与源码深度剖析

原创
作者头像
闪学it点com
发布2026-08-27 17:14:38
发布2026-08-27 17:14:38
110
举报

不堆砌代码,只抓核心脉络 —— 带你读懂Spring最精髓的设计


一、为什么需要IOC?

在传统Java开发中,对象之间的依赖关系由开发者手动管理:new Service(new Dao())。这种硬编码导致耦合度高、单元测试困难、扩展性差。

Spring IoC(Inversion of Control)容器扭转了控制权——由容器负责对象的创建、组装和生命周期管理,开发者只需声明“我需要什么”,容器自动注入。这一思想奠定了Spring生态的基石。


二、核心模型:BeanDefinition

Spring容器管理的每个对象都被抽象为一个 BeanDefinition,它存储了该Bean的所有元信息:

  • 类全限定名、作用域(singleton/prototype)
  • 构造参数、属性值、依赖关系
  • 初始化/销毁方法、懒加载标志等

代码语言:javascript
复制
// 简化版BeanDefinition核心字段
public class BeanDefinition {
    private String beanClassName;
    private String scope = "singleton";
    private boolean lazyInit = false;
    private List<PropertyValue> propertyValues;
    private ConstructorArgumentValues constructorArguments;
    // ...
}

容器本质上就是一个 Map<String, BeanDefinition>,外加一个单例缓存池。


三、容器启动流程(以AnnotationConfigApplicationContext为例)

代码语言:javascript
复制
new AnnotationConfigApplicationContext(AppConfig.class);

这行代码背后经历了三个关键阶段:

1. 扫描与注册(BeanDefinitionRegistryPostProcessor)

  • 根据 @ComponentScan 指定的包路径,通过 ClassPathBeanDefinitionScanner 扫描所有带 @Component 的类。
  • 解析 @Scope@Lazy@Primary 等注解,生成 BeanDefinition 并注册到容器。

2. 实例化前处理(BeanFactoryPostProcessor)

  • Spring 允许用户通过 BeanFactoryPostProcessor 修改 BeanDefinition 元数据(例如替换占位符 @Value("${jdbc.url}"))。
  • 关键实现:PropertySourcesPlaceholderConfigurer 在此阶段完成配置文件的解析。

3. 实例化与初始化(Bean生命周期核心)

这是最复杂的阶段,也是面试高频区。Spring 会遍历所有非懒加载的 Singleton Bean,依次执行:

代码语言:javascript
复制
实例化(构造器/工厂方法)
  → 属性填充(依赖注入)
  → 感知接口回调(BeanNameAware, BeanFactoryAware...)
  → 前置初始化(BeanPostProcessor.beforeInitialization)
  → 执行初始化方法(@PostConstruct, InitializingBean)
  → 后置初始化(BeanPostProcessor.afterInitialization)
  → 放入单例缓存池

四、依赖注入的核心:doCreateBean() 中的两段关键代码

源码中 AbstractAutowireCapableBeanFactory.createBean() 最终调用 doCreateBean(),其中两处最值得关注:

① 实例化——选择构造器

代码语言:javascript
复制
// 源码简化
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);

Spring 根据 @Autowired 标注的构造器或默认无参构造器,通过 CGLIB 或 Javassist 或反射生成对象实例(此时对象内部的属性均为 null)。

② 属性填充——解决循环依赖

代码语言:javascript
复制
populateBean(beanName, mbd, instanceWrapper);

这里会遍历 PropertyValues,对每个依赖的属性调用 resolveValueIfNecessary()。如果依赖的是另一个Bean,则触发 getBean() —— 这可能导致循环依赖。

Spring 通过 三级缓存(singletonObjects、earlySingletonObjects、singletonFactories)巧妙解决单例模式的循环依赖,其核心思想是 提前暴露未完成实例化的对象引用

代码语言:javascript
复制
// 三级缓存的核心获取逻辑(简化)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
                singletonObject = singletonFactory.getObject();
                this.earlySingletonObjects.put(beanName, singletonObject);
                this.singletonFactories.remove(beanName);
            }
        }
    }
    return singletonObject;
}

注:仅单例且允许循环依赖时,三级缓存生效。原型(Prototype)Bean 遇到循环依赖会直接抛出异常。


五、BeanPostProcessor —— 扩展的核心抓手

这是Spring留给开发者最强大的扩展点。 几乎所有的AOP、事务管理、数据源代理、自定义注解驱动,都是通过实现 BeanPostProcessor 接口完成的。

代码语言:javascript
复制
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 在初始化前修改或包装bean
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // 返回代理对象(例如AOP返回的JDK动态代理)
        return Proxy.newProxyInstance(...);
    }
}

Spring 内部大量使用该机制:

  • AutowiredAnnotationBeanPostProcessor 处理 @Autowired@Value
  • AnnotationAwareAspectJAutoProxyCreator 实现基于注解的AOP

六、非侵入式设计:你几乎看不到容器API

Spring 的设计哲学是 让容器隐藏于幕后。你编写的业务类不需要继承任何Spring类,只需标注注解即可被管理。这得益于:

  • 类型安全的依赖查找getBean(Class) 替代了早期的字符串查找。
  • 构造器注入 推荐替代字段注入,使Bean更易于单元测试。
  • 条件装配@Conditional)让配置更具弹性。

七、从源码看设计模式

设计模式

Spring中的应用

工厂模式

BeanFactory、ApplicationContext

模板方法

AbstractApplicationContext.refresh() 定义启动流程骨架,子类实现具体步骤

策略模式

多种Bean实例化策略(构造器、静态工厂、实例工厂)

代理模式

AOP、事务管理、缓存抽象

观察者模式

事件发布监听(ApplicationEvent / ApplicationListener)


八、总结:不止于容器

Spring IOC 容器远不止是一个“Map+反射”的工具,它是一个 可扩展、可插拔、生命周期完备的轻量级微内核。理解其源码,不在于记住每一行代码,而在于领悟 如何用分层抽象、后置处理器、缓存机制来构建一个松耦合的系统

真正的收获是:

一个好的框架,应当让业务代码干净纯粹,而把复杂性封装在优雅的扩展点背后。

这,正是Spring源码教给我们最宝贵的设计智慧。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、为什么需要IOC?
  • 二、核心模型:BeanDefinition
  • 三、容器启动流程(以AnnotationConfigApplicationContext为例)
    • 1. 扫描与注册(BeanDefinitionRegistryPostProcessor)
    • 2. 实例化前处理(BeanFactoryPostProcessor)
    • 3. 实例化与初始化(Bean生命周期核心)
  • 四、依赖注入的核心:doCreateBean() 中的两段关键代码
    • ① 实例化——选择构造器
    • ② 属性填充——解决循环依赖
  • 五、BeanPostProcessor —— 扩展的核心抓手
  • 六、非侵入式设计:你几乎看不到容器API
  • 七、从源码看设计模式
  • 八、总结:不止于容器
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档