Lesson 52 · Spring 生态核心原理

Spring IoC 容器原理:BeanFactory 与 ApplicationContext

高级·⭐ 必问·#Spring·#IoC·#核心

第 1 站

面试官:Spring IoC 的原理是什么?

"Spring IoC 的原理是什么?"——别只说「帮你管理对象」。面试官想听的是:BeanFactory 与 ApplicationContext 的区别、容器启动的 refresh() 十二步、BeanDefinition 元模型、Bean 创建的完整生命周期,以及三级缓存如何解决循环依赖。能说到第三层,就已经超过 90% 的候选人。

IoC(Inversion of Control)是 Spring 的灵魂,但大多数人停留在「控制反转 = 把 new 对象的权力交给容器」这一层。真正的面试深度回答需要覆盖:

  • 概念层:IoC 与 DI 的精确区分
  • 接口层:BeanFactory → ApplicationContext 的继承体系与职责差异
  • 流程层:容器启动 refresh() 的 12 步、Bean 创建的完整链路
  • 元模型:BeanDefinition 如何描述一个 Bean 的全部元信息
  • 难点:三级缓存与循环依赖(本文预告,专文展开)

本文逐站深入,从概念到源码,给出可直接在面试中复述的完整回答。

第 2 站

IoC 与 DI:不是一回事

很多人把 IoC 和 DI 混为一谈,但面试中精确区分它们是加分项:

维度IoC(控制反转)DI(依赖注入)
本质一种设计思想IoC 的一种具体实现方式
含义对象的创建、装配、生命周期由容器控制,而非程序代码直接管理容器在运行时将依赖对象「注入」到组件中
对立面程序自己 new 对象程序自己查找依赖(Service Locator 模式)
关系IoC ⊃ DI(DI 是 IoC 的子集)IoC 的实现方式之一(另一种是 Service Locator)

DI 的三种注入方式:

三种注入方式对比 · Java
// ① 构造器注入(推荐:不可变、可测试、保证非空)
public class OrderService {
    private final UserRepository userRepo;
    public OrderService(UserRepository userRepo) {
        this.userRepo = userRepo;
    }
}

// ② Setter 注入(可选依赖、运行时可变)
public class OrderService {
    private UserRepository userRepo;
    @Autowired
    public void setUserRepo(UserRepository userRepo) {
        this.userRepo = userRepo;
    }
}

// ③ 字段注入(最方便,但不推荐:不可变差、难测试)
public class OrderService {
    @Autowired
    private UserRepository userRepo;
}
面试加分点Spring 官方推荐构造器注入。理由:① 依赖不可变(final);② 保证依赖非空(构造时就失败);③ 方便单元测试(直接传 mock,无需反射)。Spring 4.3+ 单构造器可省略 @Autowired。
第 3 站

BeanFactory vs ApplicationContext:接口体系

Spring 容器的核心接口不只有一个 BeanFactory,而是一棵继承树。理解这棵树是理解容器能力的钥匙:

Spring 容器接口继承体系(简化) BeanFactory ListableBeanFactory HierarchicalBeanFactory ApplicationContext + EnvironmentCapable ApplicationContext 额外能力 i18n · 事件发布 · AOP · 资源加载 ConfigurableApplicationContext ClassPathXmlApplicationContext AnnotationConfigApplicationContext
图 1 Spring 容器接口继承体系:BeanFactory 是根,ApplicationContext 集成了 Listable + Hierarchical + 环境能力
能力BeanFactoryApplicationContext
Bean 实例化 / 依赖注入
Bean 初始化策略懒加载(getBean 时创建)启动时预创建所有单例
国际化(i18n / MessageSource)
事件发布(ApplicationEvent)
AOP 自动代理需手动配置✓ 自动集成
资源加载(Resource)
注解支持(@Autowired 等)需注册后处理器✓ 默认开启
面试金句BeanFactory 是 Spring 的「最小可用容器」,只做 Bean 的注册和获取;ApplicationContext 是「全功能容器」,在 BeanFactory 基础上叠加了企业级能力。日常开发 100% 用的是 ApplicationContext。
第 4 站

容器启动流程:refresh() 十二步

Spring 容器启动的核心入口是 AbstractApplicationContext.refresh()。这个方法只有不到 100 行,却编排了整个 Spring 的启动逻辑。面试中被问到的概率极高:

AbstractApplicationContext.java · Spring 5.x 简化
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {

        // ① 准备刷新:校验必要属性、初始化 PropertySources
        prepareRefresh();

        // ② 获取 BeanFactory:解析 XML/注解,注册 BeanDefinition
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // ③ 准备 BeanFactory:设置类加载器、注册标准 Bean
        prepareBeanFactory(beanFactory);

        try {
            // ④ 子类扩展点:注册容器级后处理器
            postProcessBeanFactory(beanFactory);

            // ⑤ 执行 BeanFactoryPostProcessor(如 PropertyPlaceholder)
            invokeBeanFactoryPostProcessors(beanFactory);

            // ⑥ 注册 BeanPostProcessor(如 AutowiredAnnotationBPP)
            registerBeanPostProcessors(beanFactory);

            // ⑦ 初始化国际化 MessageSource
            initMessageSource();

            // ⑧ 初始化事件广播器
            initApplicationEventMulticaster();

            // ⑨ 子类扩展点(如 SpringBoot 在此启动内嵌 Tomcat)
            onRefresh();

            // ⑩ 注册事件监听器
            registerListeners();

            // ⑪ 实例化所有非懒加载的单例 Bean ★最重的一步
            finishBeanFactoryInitialization(beanFactory);

            // ⑫ 发布 ContextRefreshedEvent、注册 JMX 等收尾
            finishRefresh();
        } catch (BeansException ex) {
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        }
    }
}
AbstractApplicationContext.refresh() 十二步 ① prepareRefresh() 校验属性、初始化占位符 ② obtainFreshBeanFactory() 解析配置 → BeanDefinition 注册 ③ prepareBeanFactory() 设置类加载器、表达式解析器 ④ postProcessBeanFactory() 📌 子类扩展点(模板方法) ⑤ invokeBeanFactoryPostProcessors() 执行 BeanFactory 级别后处理器 ⑥ registerBeanPostProcessors() 注册 Bean 级别后处理器 ⑦ initMessageSource() 初始化国际化资源 ⑧ initApplicationEventMulticaster() 初始化事件广播器 ⑨ onRefresh() 📌 子类扩展(SpringBoot 启动 Tomcat) ⑩ registerListeners() 注册 ApplicationListener ⑪ finishBeanFactoryInitialization() ★ 实例化所有非懒加载单例 Bean(最重的一步) ⑫ finishRefresh() 发布 ContextRefreshedEvent · Lifecycle 回调 · JMX 注册 catch: destroyBeans() → cancelRefresh() 📌 子类扩展点(模板方法) ★ 核心步骤 框架标准步骤
图 2 refresh() 十二步流程:② 解析 BeanDefinition → ⑤⑥ 注册后处理器 → ⑪ 实例化所有单例 Bean
面试追问:「第 ⑤ 步和第 ⑥ 步有什么区别?」
第 ⑤ 步执行的是 BeanFactoryPostProcessor,它作用于 BeanFactory 本身(如替换占位符 ${...});第 ⑥ 步注册的是 BeanPostProcessor,它作用于每个 Bean 实例(如 @Autowired 注入、AOP 代理)。二者执行的时机和作用对象完全不同。
第 5 站

BeanDefinition:Bean 的元数据模型

Spring 容器并不直接存储你的 Bean 类,而是存储一份描述这个 Bean 的元数据对象——BeanDefinition。就像数据库的 Schema 描述表结构一样,BeanDefinition 描述 Bean 的全部配置信息:

BeanDefinition.java · 核心属性(简化)
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    // Bean 的全限定类名
    String getBeanClassName();

    // 作用域:singleton / prototype / request / session
    String getScope();

    // 是否懒加载
    boolean isLazyInit();

    // 依赖的其他 Bean(保证创建顺序)
    String[] getDependsOn();

    // 构造器参数
    ConstructorArgumentValues getConstructorArgumentValues();

    // 属性值(setter 注入)
    MutablePropertyValues getPropertyValues();

    // 初始化 / 销毁方法
    String getInitMethodName();
    String getDestroyMethodName();
}

BeanDefinition 从哪来?三大解析路径:

配置方式解析器说明
XML 配置XmlBeanDefinitionReader解析 <bean> 标签
注解配置AnnotatedBeanDefinitionReader解析 @Component / @Bean
Java ConfigConfigurationClassPostProcessor解析 @Configuration 类

所有解析结果都注册到 BeanDefinitionRegistry(本质是一个 ConcurrentHashMap<String, BeanDefinition>):

DefaultListableBeanFactory.java · 核心存储
public class DefaultListableBeanFactory
        extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {

    // ★ BeanDefinition 的最终存储地
    private final Map<String, BeanDefinition> beanDefinitionMap
            = new ConcurrentHashMap<>(256);

    // 保持注册顺序的 Bean 名称列表
    private volatile List<String> beanDefinitionNames
            = new ArrayList<>(256);
}
BeanDefinition 生命周期公式
配置源(XML / @Component / @Bean) → BeanDefinitionReader 解析 → BeanDefinition 对象 → 注册到 BeanDefinitionRegistry(Map) → 容器启动时按 BeanDefinition 创建 Bean 实例
第 6 站

Bean 创建流程:从 getBean 到完整实例

当容器执行到 refresh() 的第 ⑪ 步 finishBeanFactoryInitialization() 时,会遍历所有非懒加载的单例 BeanDefinition,逐个调用 getBean()。完整链路如下:

AbstractBeanFactory.java · getBean 链路简化
// 入口:外部调用或容器内部触发
public Object getBean(String name) {
    return doGetBean(name, null, null, false);
}

protected <T> T doGetBean(String name, ...) {
    // ① 先查三级缓存(处理循环依赖)
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null) return (T) sharedInstance;

    // ② 检查并创建依赖的 Bean(depends-on)
    String[] dependsOn = mbd.getDependsOn();
    for (String dep : dependsOn) getBean(dep);

    // ③ 根据 scope 创建 Bean
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            return createBean(beanName, mbd, args);
        });
    }
    return (T) sharedInstance;
}
AbstractAutowireCapableBeanFactory.java · createBean 内部
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 解析 Bean 的 Class(处理 FactoryBean 等情况)
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

    return doCreateBean(beanName, mbd, args);
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // ──── 阶段 1:实例化 ────
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();

    // ★ 提前暴露到三级缓存(解决循环依赖的关键)
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

    // ──── 阶段 2:属性填充 ────
    populateBean(beanName, mbd, instanceWrapper);

    // ──── 阶段 3:初始化 ────
    Object exposedBean = initializeBean(beanName, bean, mbd);
    //   ├─ invokeAwareMethods()      → BeanNameAware, BeanFactoryAware
    //   ├─ applyBeanPostProcessorsBeforeInitialization()  → @PostConstruct
    //   ├─ invokeInitMethods()       → InitializingBean.afterPropertiesSet(), init-method
    //   └─ applyBeanPostProcessorsAfterInitialization()   → AOP 代理在此生成

    return exposedBean;
}

Bean 创建的三个阶段可以总结为:

阶段方法做了什么回调 / 扩展点
实例化createBeanInstance()反射/工厂方法/CGLIB 创建裸对象InstantiationAwareBPP.postProcessBeforeInstantiation()
属性填充populateBean()注入依赖(@Autowired, @Value)InstantiationAwareBPP.postProcessAfterInstantiation()
初始化initializeBean()Aware 回调 + BPP + init 方法BeanPostProcessor(AOP 代理在这里生成)
关键细节AOP 代理不是在「初始化之后」额外创建的,而是在 BeanPostProcessor.postProcessAfterInitialization() 中,由 AbstractAutoProxyCreator 判断是否需要代理,如果需要则返回代理对象替代原始 Bean。
第 7 站

三级缓存:循环依赖的解决思路(预告)

面试追问:「如果 A 依赖 B,B 又依赖 A,Spring 怎么解决?」——这是 Spring 面试中频率最高的深度问题之一。本文做简要预告,完整解析见专文。

Spring 用三级缓存解决单例 Bean 的循环依赖。三级缓存定义在 DefaultSingletonBeanRegistry 中:

DefaultSingletonBeanRegistry.java · 三级缓存
// 一级缓存:完整的 Bean 实例(已初始化完毕)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

// 二级缓存:早期 Bean 实例(已实例化,未填充属性)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

// 三级缓存:Bean 工厂(ObjectFactory,用于生成早期引用)
private final Map<String, ObjectFactory<?>> singletonFactories
        = new HashMap<>(16);

解决循环依赖的核心流程(以 A→B→A 为例):

  1. 创建 A:实例化 A(裸对象)→ 将 A 的 ObjectFactory 放入三级缓存
  2. 填充 A:发现依赖 B → 触发创建 B
  3. 创建 B:实例化 B(裸对象)→ 将 B 的 ObjectFactory 放入三级缓存
  4. 填充 B:发现依赖 A → 从三级缓存取 A 的 ObjectFactory → 生成 A 的早期引用 → 放入二级缓存,删除三级缓存
  5. B 完成初始化 → 放入一级缓存
  6. A 获得 B → 继续填充属性 → A 完成初始化 → 放入一级缓存
为什么需要三级而不是两级?三级缓存存的不是 Bean 实例,而是一个 ObjectFactory(Lambda 表达式)。它的作用是在需要早期引用时,通过 getEarlyBeanReference() 提前触发 AOP 代理。如果没有 AOP,两级缓存就够了;正是为了延迟代理对象的创建(只在真正被循环依赖时才创建代理),才需要第三级。
三级缓存查找顺序
getSingleton() = L1 有就返回 → L2 有就返回 → L3 有就调用 factory.getObject() 放入 L2 并删除 L3 → 返回

完整的三级缓存源码拆解、构造器注入为何无法解决循环依赖、Spring Boot 2.6+ 默认禁止循环依赖的原因,将在「Spring 循环依赖与三级缓存」专文中详细展开。

第 8 站

总结:关键接口速查 + 面试回答模板

接口 / 类职责关键方法
BeanFactory容器根接口,定义 getBean / containsBeangetBean(), isSingleton()
ApplicationContext全功能容器,集成 i18n / 事件 / AOPpublishEvent(), getMessage()
BeanDefinitionBean 的元数据描述getBeanClassName(), getScope()
BeanDefinitionRegistryBeanDefinition 的注册中心registerBeanDefinition()
BeanPostProcessorBean 初始化前后的拦截钩子postProcessBeforeInitialization(), postProcessAfterInitialization()
BeanFactoryPostProcessorBeanFactory 级别的处理器(修改 BeanDefinition)postProcessBeanFactory()
DefaultSingletonBeanRegistry单例 Bean 的三级缓存管理getSingleton(), addSingleton()
AbstractApplicationContext容器启动流程的编排者refresh()(十二步)
面试回答模板(60 秒版):
  1. 概念:IoC 是设计思想,DI 是实现方式。Spring 容器负责对象的创建、装配和生命周期管理。
  2. 接口:BeanFactory 是最小容器(懒加载),ApplicationContext 是全功能容器(预创建单例、i18n、事件、AOP)。日常用的都是 ApplicationContext。
  3. 启动:核心方法是 refresh(),十二步编排了整个启动流程——先解析 BeanDefinition,再注册后处理器,最后实例化所有单例 Bean。
  4. Bean 创建:getBean → createBean → doCreateBean,分三阶段:实例化 → 属性填充 → 初始化(含 AOP 代理)。
  5. 循环依赖:通过三级缓存解决——一级存完整 Bean,二级存早期引用,三级存 ObjectFactory(为 AOP 代理延迟创建)。
下一步「Spring 循环依赖与三级缓存」专文将深入 getSingleton() 源码、构造器注入为何无解、@Lazy 如何绕过循环依赖、Spring Boot 2.6+ 为何默认禁止循环依赖。