Spring 源码探险:容器之心

为什么是 Spring 容器?—— 一切的起点与核心

我们每天都在用 @Autowired,信手拈来,如同呼吸般自然。但你是否曾在夜深人静时扪心自问:它背后到底发生了什么?Spring 就像一个无所不知的先知,总能准确地找到我们需要的那个 Bean,不多不少,刚刚好。它是如何知道该注入什么,又是在何时完成注入的?

忘掉那些官方文档里画了又画的 UML 图,忘掉那些翻来覆去讲的理论概念。今天,我们不谈“是什么”,只究“怎么做”。我们将扮演一个冷酷的 Debugger,带上我们的显微镜,以源码为唯一的语言,以方法调用链为唯一的路径,单步跟进 Spring 容器从一个平平无奇的 Java 对象,蜕变为一个管理着整个应用生命周期的“上帝容器”的恢弘史诗。

本次探险,我们将从最经典的一行代码 new ClassPathXmlApplicationContext("bean.xml") 出发,一头扎进 Spring 的主动脉——refresh() 方法,在其中探索生命周M期的奥秘,最终在 getBean() 的终点见证一个 Bean 的诞生。

这是一场硬核的旅程,准备好了吗?🚀

一 、创世纪——ApplicationContext 的诞生前夜

一切的伟大,都源于一个微不足道的 new

// 我们的起点
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

1.1 起点:new ClassPathXmlApplicationContext("bean.xml")

当我们写下这行代码时,构造函数的世界里发生了什么?

  • 源码路径: org.springframework.context.support.ClassPathXmlApplicationContext

跟进去,你会发现一个 this() 调用链。它会一路调用父类的构造函数,像一条奔涌的河流,最终汇入 AbstractApplicationContext 的构造函数中。这是一个经典的设计模式:模板方法的体现,父类定义好骨架,子类负责实现细节。

AbstractApplicationContext 的构造函数里,有两件至关重要的事情发生:

  1. 资源定位: 我们传入的 "bean.xml" 是一个简单的字符串,Spring 如何理解它,并找到它?

  2. 源码追踪: setConfigLocations(configLocations) -> getPathMatcher() -> PathMatchingResourcePatternResolver

  3. 核心解读: Spring 将一切外部信息都抽象为 ResourceClassPathXmlApplicationContext 内部会创建一个 PathMatchingResourcePatternResolver(路径匹配资源解析器),它能理解 classpath*:ant 风格(如 com/**/service.xml)等复杂路径。它将字符串路径转换为一个或多个 Resource 对象,这体现了 Spring 对底层资源的统一抽象和封装,无论它是来自文件系统、classpath 还是网络。

  4. “发令枪” refresh() 在构造函数的最后一步,也是最关键的一步,代码会调用 refresh() 方法。

// AbstractApplicationContext.java
public AbstractApplicationContext() {
    // ... 其他准备工作
}

// ClassPathXmlApplicationContext.java
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
    // ...
    setConfigLocations(configLocations); // 设置资源路径
    refresh(); // 关键调用!
}

为什么不等对象完全构造好再手动调用初始化?因为 ApplicationContext 的职责就是作为一个已经就绪的、可用的容器。它的诞生,必须伴随着自身的初始化完成。refresh() 就是这声“发令枪”,一旦打响,容器的创世之旅便正式开始。

refresh() —— Spring 容器的“核心引擎”与“生命周期”

refresh() 方法是 Spring 容器的心脏,是整个 IoC 容器初始化和生命周期管理的绝对核心。它位于 AbstractApplicationContext 中,包含了 13 个清晰的步骤,如同一份构建蓝图,精确地定义了容器的启动流程。

让我们深入这座“引擎室”,逐一拆解这 13 个步骤。

  • 源码路径: org.springframework.context.support.AbstractApplicationContext#refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 1. 准备阶段
        prepareRefresh();

        // 2. 获取新的 BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 3. 准备 BeanFactory
        prepareBeanFactory(beanFactory);

        try {
            // 4. BeanFactory 的后置处理
            postProcessBeanFactory(beanFactory);

            // 5. 调用 BeanFactoryPostProcessors
            invokeBeanFactoryPostProcessors(beanFactory);

            // 6. 注册 BeanPostProcessors
            registerBeanPostProcessors(beanFactory);

            // 7. 初始化 MessageSource (国际化)
            initMessageSource();

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

            // 9. 特殊 Bean 的初始化 (留给子类实现)
            onRefresh();

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

            // 11. 实例化所有非懒加载的单例
            finishBeanFactoryInitialization(beanFactory);

            // 12. 完成刷新,发布事件
            finishRefresh();
        }
        catch (BeansException ex) {
            // ... 异常处理
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        }
        finally {
            // ... 重置缓存
            resetCommonCaches();
        }
    }
}

2.1 第一阶段:容器的准备与扫描 (步骤 1-3)

这是“备料”阶段,为后续的加工做准备。

  1. prepareRefresh() 设置容器的启动时间、激活状态标志,并对一些属性进行早期验证。非常直白。

  2. obtainFreshBeanFactory() 分水岭! 这是 BeanFactory 诞生的时刻。

  3. 核心解读: 此方法会先销毁旧的 BeanFactory(如果存在),然后创建一个全新的 DefaultListableBeanFactory。接着,它会调用 loadBeanDefinitions(),这是一个抽象方法。对于我们的 ClassPathXmlApplicationContext,它会委托 XmlBeanDefinitionReader解析 bean.xml 文件

  4. XmlBeanDefinitionReader 的工作: 它会逐行读取 XML,将每一个 <bean> 标签的信息(id, class, scope, <property> 等)解析并封装成一个 BeanDefinition 对象。

  5. BeanDefinition 是什么? 它是 Spring Bean 的“蓝图”或“档案”。它不是 Bean 实例本身,而是描述了 Bean 应该如何被创建的元数据。

  6. 注册: 最终,这些 BeanDefinition 对象被注册到一个名为 beanDefinitionMapConcurrentHashMap 中。keybeanNamevalueBeanDefinition 对象。

    至此,IoC 容器的数据基础已经奠定。容器知道了它需要管理哪些 Bean,以及如何创建它们。

  7. prepareBeanFactory(beanFactory)BeanFactory 添加“佐料”。

    • 核心解读: 此时的 BeanFactory 还很“素”。这一步会给它设置一些默认组件,比如 ClassLoader,表达式解析器 SpelExpressionParser,以及添加几个特殊的 BeanPostProcessor(我们后面会详谈)。

2.2 第二阶段:BeanFactory 的“增强”与“扩展点” (步骤 4-5)

这是 Spring 提供给我们的第一个黄金扩展点

  1. postProcessBeanFactory(beanFactory) 这是一个经典的模板方法,设计为子类去重写。例如,AnnotationConfigApplicationContext 就会在这里添加处理 @Configuration 注解的后置处理器。AbstractApplicationContext 在这里提供了一个空的实现,其目的就是为了让子类去重写,从而在 BeanFactory 初始化之后、任何 Bean 实例化之前,添加一些子类特有的功能。例如,WebApplicationContext 就会重写此方法,注册 requestsession 等 Web 专属的 scope,并添加对 ServletContext 的感知等。

  2. invokeBeanFactoryPostProcessors(beanFactory) 极其重要!

    这个方法的执行逻辑,并非一个简单的循环调用。其内部由一个名为 PostProcessorRegistrationDelegate 的静态代理类来精心编排,整个过程充满了优先级排序和阶段划分,其复杂和严谨程度,远超想象。

    首先,Spring 在此阶段处理两种类型的处理器,它们的能力和职责有本质区别:

  3. BeanDefinitionRegistryPostProcessor (BDRPP): 这是 BeanFactoryPostProcessor 的一个子接口。它更强大,除了拥有 BeanFactoryPostProcessor 的所有能力外,还额外拥有一个核心方法:postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)

    • 核心能力可以动态地注册、修改、移除 BeanDefinition。它能改变 BeanFactoryBeanDefinition 的“数量和结构”。
    • 典型代表ConfigurationClassPostProcessor,它负责扫描 @Configuration 类,解析 @ComponentScan@Bean,然后将成百上千个新的 BeanDefinition 注册到容器中。
  4. BeanFactoryPostProcessor (BFPP):

    • 核心能力只能读取和修改已存在的 BeanDefinition 的元数据(比如修改某个属性值)。它不能增删 BeanDefinition。它只能改变 BeanDefinition 的“内容”。
    • 典型代表PropertySourcesPlaceholderConfigurer,它负责将 ${...} 占位符替换为真实的属性值。

核心结论BDRPP 是“地图绘制者”,能画出新的区域;BFPP 是“地图美化者”,只能在已有的地图上进行标注。因此,Spring 必须先执行所有 BDRPP,再执行 BFPP,确保地图都画好了,才能开始美化。

在方法的一开始,定义了几个关键的 List,用于在处理过程中分类存放不同类型的处理器:

   // 用于存放已执行的 BeanDefinitionRegistryPostProcessor 的名字,防止重复执行
   Set<String> processedBeans = new HashSet<>();
   // 存放硬编码(或由 ApplicationContext 直接添加)的 BDRPP
   List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
   // 存放硬编码的普通 BFPP
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();

执行流程的逐行解剖

步骤 1:处理硬编码的 BeanDefinitionRegistryPostProcessor

方法首先会处理那些不是作为 Bean 定义,而是被程序直接 addBeanFactoryPostProcessor() 添加进来的处理器。

   // 循环遍历所有硬编码的后处理器
   for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
       if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
           // 如果是 BDRPP 类型
           BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
           // 直接调用其 postProcessBeanDefinitionRegistry 方法
           registryProcessor.postProcessBeanDefinitionRegistry(registry);
           // 并将其存入 registryProcessors 列表,以便后续调用其 postProcessBeanFactory 方法
           registryProcessors.add(registryProcessor);
       } else {
           // 如果是普通的 BFPP,则先存起来,稍后处理
           regularPostProcessors.add(postProcessor);
       }
   }

步骤 2:处理作为 Bean 定义的 BeanDefinitionRegistryPostProcessor

这是最复杂也最精妙的部分。Spring 需要从 BeanFactory 中找出所有被定义为 Bean 的 BDRPP,并严格按优先级执行。

   // 用于存放当前需要处理的 BDRPP
   List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

   // 第一优先级:处理实现了 PriorityOrdered 接口的 BDRPP
   String[] postProcessorNames = registry.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
   for (String ppName : postProcessorNames) {
       if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
           // 获取 Bean 实例并添加到当前处理列表
           currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
           processedBeans.add(ppName);
       }
   }
   // 排序并调用
   sortPostProcessors(currentRegistryProcessors, beanFactory);
   registryProcessors.addAll(currentRegistryProcessors);
   invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
   // 清空当前列表,为下一优先级做准备
   currentRegistryProcessors.clear();

   // 第二优先级:处理实现了 Ordered 接口的 BDRPP
   // ... (代码逻辑与上面类似,只是 isTypeMatch 的判断条件变为 Ordered.class) ...
   // ... (排序、调用、清空) ...

   // 第三优先级:处理所有其他的 BDRPP
   // ... (在一个循环中不断查找和处理,直到没有新的 BDRPP 出现为止) ...
   // 这个循环是为了处理在一个 BDRPP 中注册了另一个 BDRPP 的场景
   // ... (排序、调用、清空) ...

这个过程的核心是,分三次从容器中捞取不同优先级的 BDRPP Bean,并立即执行它们的 postProcessBeanDefinitionRegistry 方法。ConfigurationClassPostProcessor 就是在第一优先级(它实现了 PriorityOrdered)被获取并执行的。

步骤 3:调用所有处理器的 postProcessBeanFactory 方法

至此,所有 BeanDefinition 的注册工作已经全部完成,“地图”已经绘制完毕。现在,开始执行所有 BFPP 的逻辑(包括那些 BDRPP,因为它们也是 BFPP)。

   // 首先,调用所有已执行过的 BDRPP 的 postProcessBeanFactory 方法
   invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
   // 然后,调用所有硬编码的普通 BFPP 的方法
   invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

   // 最后,按照与步骤2相同的优先级逻辑,从容器中找出所有普通的 BFPP Bean
   // 并调用它们的 postProcessBeanFactory 方法
   // ... (处理 PriorityOrdered 的 BFPP -> 处理 Ordered 的 BFPP -> 处理其他的 BFPP) ...

| 执行阶段 | 处理器类型 | 执行顺序 | 核心任务 | 经典案例 |
| ———— | ————————————- | ———————————————————— | ————————————————– | ———————————————————– |
| 阶段一 | BeanDefinitionRegistryPostProcessor | 1. 硬编码的<br>2. 作为Bean的 (按 PriorityOrdered -> Ordered -> 普通 排序) | 注册/修改/移除 BeanDefinition (改变蓝图结构) | ConfigurationClassPostProcessor (处理注解配置) |
| 阶段二 | BeanFactoryPostProcessor | 1. 已执行过的BDRPP<br>2. 作为Bean的 (按 PriorityOrdered -> Ordered -> 普通 排序) | 修改 BeanDefinition 的元数据 (改变蓝图内容) | PropertySourcesPlaceholderConfigurer (替换${...}占位符) |

为什么这个设计如此重要?

这个看似复杂的流程,保证了 Spring 容器的极致可扩展性。它确保了:

  • 配置的确定性:所有 Bean 的定义(“有什么 Bean”)都在任何 Bean 属性被修改(“Bean 的内容是什么”)之前被完全确定下来。

  • 功能的正交性:负责“扫描注册”的处理器和负责“属性修改”的处理器被清晰地分离开来,各司其职。

  • 强大的扩展能力:开发者可以编写自己的 BDRPPBFPP,精确地在容器生命周期的这个黄金扩展点介入,实现各种自定义的框架功能,例如动态注册 Bean、加密属性解密等。

@Configuration 的秘密:既然说到@Configuration 注解的后置处理器又提到BeanDefinitionRegistryPostProcessor,那就不得揭秘一下容器是怎么感知@Configuration@Bean等注解然后注册到容器中的。

ConfigurationClassPostProcessor 也是一个 BeanFactoryPostProcessor。它会在这一步扫描所有 @Configuration 类,解析 @Bean, @Import, @ComponentScan 等注解,然后将它们也转化为新的 BeanDefinition 注册到容器中我们钻入 ConfigurationClassPostProcessor 的核心方法 postProcessBeanDefinitionRegistry,看看它是如何“绘制地图”的。processConfigBeanDefinitions 的两大阶段

这个方法的工作可以清晰地分为两个阶段:解析(Parse)加载(Load)

阶段一:解析(Parse)—— 将注解翻译成内部模型

  1. 创建解析器:方法内部首先会创建一个 ConfigurationClassParser。这个解析器是所有解析工作的入口。
  2. 收集候选者:它会遍历 BeanDefinitionRegistry 中所有已注册的 BeanDefinition,找出那些被标记为 @Configuration 的“配置类候选者”。
  3. 执行解析:调用 parser.parse(candidates)parser 会对候选者集合进行循环处理,对每一个配置类,它会调用 doProcessConfigurationClass 方法。
  4. doProcessConfigurationClass 的内部细:这是真正的“翻译”工作发生的地方。
    • 处理 @Conditional:在处理任何注解前,它会先通过 shouldSkip() 方法检查类上的 @Conditional@Profile 注解,如果条件不满足,整个类都会被跳过。
    • 处理 @PropertySource:解析注解,并将指定的属性文件加载到 Environment 中。
    • 处理 @ComponentScan:创建一个 ComponentScanAnnotationParser,它会根据注解指定的包路径,使用 ClassPathBeanDefinitionScanner 去扫描,找到所有带 @Component@Service@Repository 等注解的类,并将它们初步解析为 BeanDefinition 注册到容器中,同时返回这些类供后续处理。
  5. 处理 @Import:这是最复杂的部分。
  6. 如果导入的是另一个 @Configuration 类,则递归调用解析流程。
  7. 如果导入的是 ImportSelector,则实例化它并调用其 selectImports() 方法,获取一个类名数组,然后对这些类进行递归解析。
  8. 如果导入的是 ImportBeanDefinitionRegistrar,则先不执行,而是将它暂存起来,等待后续加载阶段再回调。
  9. 处理 @Bean:它不会立刻将 @Bean 方法转为 BeanDefinition,而是扫描类中的所有 @Bean 方法,将它们的信息封装成 BeanMethod 对象,存入 ConfigurationClass 对象的 beanMethods 集合中。
  10. 处理接口的 default 方法:还会处理接口中的 default @Bean 方法。

经过这个阶段,所有的注解信息都被翻译并存储在了一系列 ConfigurationClass 对象中。此时,除了 @ComponentScan 扫描到的组件外,@Bean 方法还未成为真正的 BeanDefinition

阶段二:加载(Load)—— 将内部模型物化为 BeanDefinition

  1. 创建加载器:解析阶段完成后,processConfigBeanDefinitions 方法会创建一个 ConfigurationClassBeanDefinitionReader

  2. 执行加载:调用 reader.loadBeanDefinitions(configClasses)。这个加载器会遍历第一阶段生成的所有 ConfigurationClass 对象集合。

  3. loadBeanDefinitionsForConfigurationClass:对于每一个 ConfigurationClass:

  4. 加载 @Bean 方法:调用 loadBeanDefinitionsForBeanMethod(),为每个 @Bean 方法创建一个 ConfigurationClassBeanDefinition,这是一个特殊的 BeanDefinition,它记录了该 Bean 是由哪个工厂方法(@Bean 方法)和哪个工厂 Bean(@Configuration 类)创建的,然后将其注册到 BeanDefinitionRegistry 中。

  5. 加载 @ImportImportBeanDefinitionRegistrar:回调之前暂存的 ImportBeanDefinitionRegistrar 实例,执行其 registerBeanDefinitions() 方法,让它可以编程式地注册任意数量的 BeanDefinition

  6. 加载 @ImportResource 注解指定的 XML 文件。

至此,ConfigurationClassPostProcessor 的使命才算真正完成。它通过“解析-加载”两步走,将我们书写的、人类易于理解的注解,系统性、结构化地转换为了 Spring IoC 容器能够理解的底层数据结构——BeanDefinition

2.3 第三阶段:Bean 的“加工厂”—— BeanPostProcessor 的注册 (步骤 6)

第二个黄金扩展点,为后续所有 Bean 的生命周期埋下伏笔。

  1. registerBeanPostProcessors(beanFactory)
  2. 核心解读: 此方法会找出容器中所有实现了 BeanPostProcessor 接口的 Bean,然后将它们实例化并添加到 BeanFactorybeanPostProcessors 列表中。
  3. 排序: 它还会对这些 BeanPostProcessor 进行排序,遵循 PriorityOrdered > Ordered > 无序的规则。
  4. BeanPostProcessor 是什么? 它是 Bean 的后置处理器。与 BeanFactoryPostProcessor 不同,它干预的是 Bean 实例化过程本身,在 Bean 初始化前后提供切入点。我们熟知的 AOP、@Autowired 的实现都离不开它。

2.4 第四阶段:应用上下文功能准备 (步骤 7-10)

这部分是为 ApplicationContext 相对于 BeanFactory 额外提供的企业级功能做准备。

  • initMessageSource(): 初始化国际化支持。
  • initApplicationEventMulticaster(): 初始化事件广播器。这是一个典型的观察者模式实现,负责管理所有 ApplicationListener 并向它们派发事件。
  • onRefresh(): 一个模板方法,留给子类在特定时机执行特殊 Bean 的初始化。
  • registerListeners(): 从容器中找出所有 ApplicationListener Bean,并将它们注册到广播器中。

2.5 第五阶段:创世的终章——实例化所有“非懒加载”单例 (步骤 11)

refresh() 中最激动人心的一步!

  1. finishBeanFactoryInitialization(beanFactory)
  2. 核心解读: 此方法的核心是调用 beanFactory.preInstantiateSingletons()
  3. preInstantiateSingletons() 它会遍历 beanDefinitionMap 中所有的 BeanDefinition,筛选出所有非抽象、单例且非懒加载的 Bean,然后调用 getBean(beanName) 来触发它们的创建。
  4. 这是一个“急切”的动作,确保在容器启动完成后,所有核心的单例 Bean 都已经准备就绪,立即可用。这个调用将直接把我们的源码探险引导至下一章的核心——getBean()

2.6 终曲:发布容器就绪事件 (步骤 12)

  1. finishRefresh()
  2. 核心解读: 清理资源,最重要的是发布一个 ContextRefreshedEvent 事件。所有监听了这个事件的 ApplicationListener(比如 WebServerStartStopLifecycle)都会被触发,标志着容器已完全就绪

至此,refresh() 的 13 个步骤走完。一个功能完备、管理着所有单例 Bean 的 IoC 容器正式诞生!

getBean() —— 一个 Bean 的诞生、依赖与宿命

如果说 refresh() 是宏观的构建过程,那么 getBean() 就是微观的创造过程。它回答了终极问题:一个 Bean 究竟是如何从 BeanDefinition 变成一个活生生的 Java 对象的?

3.1 入口:getBean(String name)

当我们调用 context.getBean("userService") 时,旅程开始了。

  • 源码路径: org.springframework.beans.factory.support.AbstractBeanFactory#getBean(String)

这个方法是一个入口,它会做一些转换和检查,然后将调用委托给核心方法 doGetBean()

// AbstractBeanFactory.java
protected <T> T doGetBean(
        String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
        throws BeansException {
    // ...
}

3.2 核心逻辑:doGetBean 的三级缓存与循环依赖

doGetBean 的逻辑非常复杂,但其精髓在于对单例 Bean 的处理,尤其是三级缓存的设计,这是 Spring 用来解决循环依赖问题的神来之笔。

  • 源码路径: org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

DefaultSingletonBeanRegistry 中,定义了三个 Map:

  1. singletonObjects (一级缓存): Map<String, Object>
  2. 用途: 存放已经完整初始化好的单例 Bean(成品)。getBean 请求首先会检查这里。
  3. earlySingletonObjects (二级缓存): Map<String, Object>
  4. 用途: 存放早期暴露的单例 Bean。这些 Bean 已经实例化,但尚未填充属性(半成品)。
  5. singletonFactories (三级缓存): Map<String, ObjectFactory<?>>
  6. 用途: 存放用于创建早期暴露 Bean 的工厂 (ObjectFactory)。

循环依赖解决流程(以 A 依赖 B,B 依赖 A 为例):

  1. getBean("a") 开始,检查三级缓存,均未命中。
  2. 开始创建 A。首先将一个 ObjectFactory 放入三级缓存 singletonFactories 中。这个工厂的作用是,如果有人需要,它可以立刻创建一个 A 的“半成品”对象(通过 getEarlyBeanReference,这里是 AOP 代理介入的关键时机)。
  3. 对 A 进行属性填充 (populateBean)。发现 A 依赖 B,于是触发 getBean("b")
  4. getBean("b") 开始,检查三级缓存,均未命中。
  5. 开始创建 B。同样,将 B 的 ObjectFactory 放入三级缓存
  6. 对 B 进行属性填充,发现 B 依赖 A,于是触发 getBean("a")
  7. 关键点来了!getBean(“a”) 再次被调用。此时:
  8. 检查一级缓存 singletonObjects,没有。
  9. 检查二级缓存 earlySingletonObjects,没有。
  10. 检查三级缓存 singletonFactories命中了!我们找到了在第 2 步放入的 A 的工厂。
  11. 通过这个工厂,立刻创建一个 A 的早期引用(如果需要 AOP,此时会创建代理对象),并将这个早期引用从三级缓存中移除,放入二级缓存 earlySingletonObjects 中。
  12. getBean("a") 返回了这个早期引用。B 顺利完成了属性填充,成为一个完整的 Bean。
  13. getBean("b") 执行完毕,将完整的 B 对象放入一级缓存 singletonObjects 中,并返回。
  14. 回到第 3 步,A 拿到了 B 的实例,也顺利完成了属性填充。
  15. A 也成为一个完整的 Bean,getBean("a") 的最外层调用执行完毕,将完整的 A 对象放入一级缓存

循环依赖被解开!三级缓存的设计核心在于:利用一个对象工厂,将实例化和属性填充这两个步骤分开,允许在对象未完全创建好时,提前暴露一个引用。

3.3 Bean 的生命周期全景图:从 createBean 到销毁

如果缓存中没有,Spring 就会调用 createBean 来从零开始创造一个 Bean。

  • 源码路径: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean

这是一个 Bean 生命周期的微观缩影:

  1. createBeanInstance() 实例化。通过反射调用构造函数,或者工厂方法,new 一个对象出来。此时它只是一个“裸”对象。
  2. populateBean() 属性填充。这是依赖注入发生的地方。
  3. AutowiredAnnotationBeanPostProcessor 等后置处理器会在这里大显身手,扫描对象的所有字段和方法,如果发现 @Autowired 注解,就去容器中 getBean() 相应的依赖,然后通过反射设置到当前对象上。
  4. initializeBean() 初始化。这是 Bean 得以“感知”容器,并执行自定义逻辑的地方。
  5. invokeAwareMethods() 如果 Bean 实现了 BeanNameAware, BeanFactoryAware 等接口,此时会调用这些接口的方法,将容器自身的信息注入给 Bean。
  6. applyBeanPostProcessorsBeforeInitialization() 执行所有 BeanPostProcessorpostProcessBeforeInitialization 方法。
  7. invokeInitMethods() 调用 Bean 自己定义的初始化方法(@PostConstruct 注解的方法或 afterPropertiesSet 方法或 XML 中的 init-method)。
  8. applyBeanPostProcessorsAfterInitialization() 执行所有 BeanPostProcessorpostProcessAfterInitialization 方法。这是 AOP 代理发生的关键节点!

3.4 AOP 的切入时机

  • 源码路径: org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization

initializeBean 的最后一步,AbstractAutoProxyCreator(一个 BeanPostProcessor)会检查当前初始化的 Bean。

  • 核心解读: 它会拿着容器中所有的 Advisor (切面),去和当前 Bean 的所有方法进行匹配。如果发现至少有一个方法匹配成功,就意味着这个 Bean 需要被代理
  • 接着,它会创建一个 ProxyFactory,根据情况选择 JDK 动态代理(如果目标类实现了接口)或 CGLIB(如果目标类没有实现接口),生成一个代理对象。
  • 最后,这个代理对象会替换掉原始的 Bean 对象,并返回给容器。
  • 从此以后,任何对这个 Bean 的调用,实际上都是在调用这个代理对象,AOP 的逻辑(Advice)也因此得以在目标方法前后执行。

四 、总结 —— 从源码看设计,从设计看思想

当我们从源码的深渊中探出头来,回望整个 Spring 容器,我们看到的不再是零散的注解和配置,而是一幅宏伟的设计蓝图。

4.1 设计模式的盛宴

Spring 容器就是一本活生生的设计模式教科书:

  • 工厂模式: BeanFactory 本身就是最典型的工厂。
  • 模板方法模式: refresh(), createBean() 等骨架方法,将流程固定,将变化点开放给子类。
  • 观察者模式: ApplicationEventApplicationListener 构成了 Spring 的事件驱动模型。
  • 代理模式: Spring AOP 的核心实现。
  • 策略模式: InstantiationStrategy (实例化策略)、BeanNameGenerator (Bean 名称生成策略)等,允许用户在特定环节替换算法。
  • 单例模式: DefaultSingletonBeanRegistry 完美地管理着所有单例 Bean

4.2 Spring 的核心思想

源码之下,是 Spring 深刻的架构思想:

  • 控制反转 (IoC): 整个流程都是容器在主动推进,Bean 的创建、依赖的获取,都由容器控制,应用程序只是被动接受。
  • 面向切面 (AOP):BeanPostProcessor 为核心,通过代理技术,实现了对业务代码的无侵入式功能增强。
  • 面向接口编程: 整个容器充满了扩展点,BeanFactoryPostProcessor, BeanPostProcessor, Aware 接口… 它们共同构建了一个极度灵活、可插拔的系统。
  • 万物皆可抽象:Resource 对资源的抽象,到 BeanDefinition 对 Bean 的抽象,再到 Environment 对环境的抽象,Spring 善于创建上层抽象来屏蔽底层差异,提供统一的视图。

通过这次旅程,我们不再仅仅是 Spring 的使用者,我们成为了它的理解者。当我们再次写下 @Autowired 时,脑海中浮现的将是三级缓存的精妙舞蹈;当我们配置 AOP 时,心中了然的是 BeanPostProcessor 在生命周期末端的悄然一瞥。

这,仅仅是 Spring 探险的第一站。容器是心脏,但 Spring 的世界远不止于此。下一站,我们将深入 AOP 的迷雾,或是探索 Spring MVC 的请求之旅。