🍃Spring源码阅读-IOC

type
status
date
slug
summary
tags
category
icon
password
原文
 
IOC 容器负责对象的创建、初始化、销毁等生命周期。对象直接通过 new 关键字创建依赖的组件,导致类与类之间紧密耦合。
 
容器就是存放 Bean 的地方,创建一个 Bean,首先需要 Bean的信息定义和读取。Bean可以使用 XML、注解主要方式。(Spring已经是注解驱动编程了)
BeanDefinitionReader 这个接口中是Spring 框架中用于加载和解析 Bean 定义的核心接口,它的主要作用是将不同形式的配置源(如 XML、注解、Groovy 等)转换为 Spring 容器可识别的 BeanDefinition(Bean 元数据) 对象,然后Spring 框架根据这些信息去完成实例化和初始化 Bean。
销毁 Bean这一步是在容器销毁时调用,但通常在业务开发中几乎使用不到。
refresh() 容器启动核心方法
 

一、Bean

Bean的生命周期指的是实例化→初始化→销毁的过程。完成初始化的非延迟加载的单例Bean,会放入单例池,之后使用直接通过上下文的 getBean方法获取实例。

1.1 Bean 的解析与读取

 
1. Bean的元数据
通常说 Bean 的元数据指的是BeanDefinitionBeanDefinition是一个接口,它主要定义了以下关于 Bean 的以下内容
  • Bean 的类型和作用域
  • 构造参数和属性值设置
  • 初始化和销毁方法
  • 依赖关系和自动装配模式
  • 生命周期相关配置(懒加载、依赖对象等)
 
 

二、Bean的生命周期概述

IOC容器的启动入口在AbstractApplicationContext#refresh() 方法。在这里容器会读取Bean 上的注解和配置配件,转化成Bean 的元信息。然后容器会根据得到的Bean 的元信息,实例化Bean和初始化 Bean,Bean初始化前后会执行 BeanPostProcessor这样一个可用的 Bean 就被创建出来了。同时 Bean 还可以定义销毁的方法供容器销毁时调用。

1.Bean容器

Bean容器最核心的两个类:BeanFactoryDefaultListableBeanFactory

1.1 BeanFactory

BeanFactory是容器的根接口,定义了 IOC 容器的基础能力,所有 Bean 的注册、依赖注入、生命周期管理均由其实现。提供了获取 Bean(这里的 Bean 包含普通 Bean 和 FactroryBean)实例等方法。常用的从上下文中获取 Bean 实例的方法就是这里定义的。

1.2 DefaultListableBeanFactory

DefaultListableBeanFactory最核心的 IOC 容器实现类,直接实现了 BeanFactory 接口, 提供完整的 Bean 定义注册、依赖解析、作用域管理、Bean 生命周期管理功能。所有 Bean 定义(BeanDefinition)存储在 Map<String, BeanDefinition> beanDefinitionMap 中。

2.Bean的解析

这一步主要的工作是将 bean的元数据(BeanDefinition)进行提取,扫描指定包路径下的类,识别 @Component@Service@Repository@Controller 等注解,解析 @Bean 方法,生成 BeanDefinition ,放入到容器中。简而言之就是解析配置并注册 BeanDefinition。

2.1 BeanDefinitionReader

BeanDefinitionReader 接口定义了读取Bean 的一些方法。对于不同的来源(json、xml、注解)可以使用不同的实现类来读取 Bean信息。
  • ConfigurationClassPostProcessor 解析 @Configuration@ComponentScan@Bean 等注解的核心处理器。
  • ConfigurationClassParser 解析配置类的元数据(如 @ComponentScan 的包路径、@Bean 方法)。
  • ClassPathBeanDefinitionScanner 扫描类路径,识别并注册 @Component 注解的类为 BeanDefinition
  • ConfigurationClassBeanDefinitionReader 将 @Bean 方法和其他动态生成的 BeanDefinition 注册到容器。
BeanDefinition 是Bean的元数据,具体包含了Bean 的初始化方法、销毁方法、name、scope、class、dependsOn、是否是 FactoryBean等等信息,知道这些信息容器才能够生成 Bean 的实例。

3.Bean的实例化

经过前面的信息,已经拿到了Bean的元数据,现在可以通过这些元信息进行实例化,通过拿到BeanDefinition 拿到了构造器,反射生成 Bean
实例化和初始化是不同的概念,实例化可以理解为在堆上申请了一个空间,生成一个对象实例,此时对象的属性值一般是默认值。初始化是指给对象的属性赋值的过程。
在实例化前后有可以干预 Bean 的生命周期的扩展接口InstantiationAwareBeanPostProcessor 5.2.x 以后推荐使用SmartInstantiationAwareBeanPostProcessor

4.Bean 的初始化

初始化是配置对象实例的过程,包括依赖注入(属性填充)、执行初始化回调方法等,触发时机是在实例化之后。
核心方法AbstractAutowireCapableBeanFactory#createBeanInstance() ,这里是 SpringBean的容器根据 Bean 的元信息通过获取Bean的构造器反射创建Bean的核心方法。
实例化对象有很多细节,但是主要的关注点是 Bean 的Scope和依赖的问题的解决。
大致的调用链如下。

4.1 依赖注入

在之前 Bean 已经完成了实例化,在属性填充这个步骤,这里的步骤主要的工作是解析依赖并完成Bean 对应的属性设置值。核心方法AbstractAutowireCapableBeanFactory#populateBean()
这里的 Bean 分为两类,容器 Bean和普通 Bean。
  • 容器对象属性填充
    • 主要的方法在AbstractAutowireCapableBeanFactory#invokeAwareMethods
  • 普通 Bean 对象
    • 普通的 Bean 要解析类中的@Autowired/@Value 上的注解,找到相关依赖类,通过反射 设置 Bean 属性值。
 

4.2 BeanPostProcessor

在Bean初始化前后还有一个BeanPostProcessor 的处理过程,主要是对Bean的功能的一个增强,例如对在初始化前后对 Bean 的状态或状态进行修改,是Spring对 Bean的生命周期的核心扩展地方。BeanPostProcessor 也是实现 AOP 的关键。
  1. BeanPostProcessor 类的增强器
    1. 定义了两个核心接口
      • postProcessBeforeInitialization(Object bean, String beanName)
        • 在 Bean 的初始化方法(如 @PostConstructInitializingBean.afterPropertiesSet() 或自定义的 init-method之前调用。
          可对 Bean 实例进行修改(如属性调整)或返回包装后的代理对象。
      • postProcessAfterInitialization(Object bean, String beanName)
        • 在 Bean 的初始化方法之后调用。
          通常用于生成代理对象(如 AOP)或最终处理。
BeanPostProcessor(增强器)在核心方法 refresh方法中触发,invokeBeanFactoryPostProcessors()先将应用中的所有BeanPostProcessors 先注册上,然后在finishBeanFactoryInitialization() 类的初始化前后调用。

4.3 InitializingBean接口

用于 Bean属性注入完成后执行自定义的初始化逻辑,其核心方法为 afterPropertis(),会在依赖注入完成后、Bean 正式投入使用前,被 Srping容器自动调用(在invokeInitMethods 中会触发这个调用

FactoryBean 和 BeanFactory

BeanFactory是容器的根接口,定义了 IOC容器的基本能力。FactoryBean 是一种特殊 Bean,用于生成其他 Bean。FactoryBean 也是被容器管理的 Bean,用于扩展 Bean的创建逻辑,封装复杂初始化的过程。如整合 MyBatis时,SqlSeessionFactoryBean 用于生成SqlSessionFactory。

Spring循环依赖问题

Spring 在 5.3x 版本以后已经不支持循环依赖。这里的代码都是 5.2.x

循环依赖能解决的前提

1. 实例化和初始化独立

最基本的前提是实例化和初始化这两个步骤分开,两个阶段的独立意味着可以将早期引用存储到三个缓存中来解决循环依赖问题。

2. 仅能支持单例模式

原型模式下,每次 getBean 都是新的实例,Bean 不会存入到缓存中,所以不支持原型模式下的循环依赖。当然这里指的是互相依赖的两个 Bean 都是原型模式

3. 不支持构造器注入

构造器注入意味着无法提前暴露对象,所以无法实例化两个互相依赖的Bean。

Bean的实例化的调用链

循环依赖解决流程

一、创建 Bean A

  • 检查一级缓存中是否有 A,未找到 Bean A。标记 Bean A 为正在创建的对象(singletonsCurrentlyInCreation 加入 a)
  • 实例化 Bean A,通过反射创建 Bean A的实例(此时 Bean A的创建变量 beanB 还没注入)
  • 将Bean A 的ObejectFactory 放入三级缓存(暴露 Bean A 的早期引用),然后依赖注入 Bean B 时,发现没有Bean B,这个时候 Bean B还没实例化,此时触发创建Bean B(getBean(”b”))。

    二、创建 Bean B

    1. 创建 A 的步骤类似,检查缓存→实例化 Bean B→将 Bean B的 ObjectFactory放入到三级缓存。
    1. 依赖注入,将 Bean A 注入到 Bean B。
        • 一级缓存中无 Bean A
        • 检查到 Bean A 正在创建中(singletonsCurrentlyInCreatio(”a“)
        • 从三级缓存中获取到了 Bean A的 ObjectFactory,调用 ObjectFactory的 getObject(”a”)方法获取 Bean A的早期引用存到二级缓存中且把三级缓存中的Bean A的ObjectFactory进行移除。
      1. 完成 Bean B 的初始化
          • Bean B 成功注入 Bean A的早期引用。
          • 执行 Bean B的初始化逻辑(属性注入,@PostConstruct等)
          • 将 Bean B 存入一级缓存中。

      三、完成 Bean A的初始化

      • Bean B 已经存入到一级缓存中,直接注入 Bean A
      • 完成 Bean A的初始化逻辑(属性注入,@PostConstruct等)
      • 将 Bean A存入到一级缓存,并清除二级缓存中的早期引用。
      💡
      hints: 早期引用就是半成品(或者叫半初始化状态),Bean 的已经实例化,但是属性还没有赋值。

      循环依赖相关问题

      一层或两层缓存能不能解决循环依赖问题

      结论可以,两层的肯定是可以的,只要将半初始化对象先放到二级缓存中即可。
      一层缓存需要对 Bean 做一定的改动,需要给Bean定义一个属性标识Bean是否是半初始化状态,但是这样的处理会非常麻烦,按照 Spring的设计一级缓存都是放完整初始化的 Bean,所以半初始化的Bean 是没有放置的地方,只能够采取Bean 的属性标识来解决循环依赖的问题。
       
      Prev
      Spring源码阅读笔记
      Next
      Spring源码阅读-AOP
      Loading...