Spring源码分析—BeanFactory

Spring 源码分析——BeanFactory

本篇文章讨论 Spring 源码中 BeanFactory 和 BeanDefinition 的设计实现,分析 Spring 如何创建单例 Bean 和如何使用三级缓存解决循环依赖。

对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/01-bean-factory

Spring源码分析—BeanFactory
image-20230413163240313

概括

  1. BeanFactory 接口定义了getBean()方法,BeanFactory 定义了获取 Bean 的父层函数;
  2. SingletonBeanRegistry 接口定义了getSingleton()方法,主要负责管理单例 Bean;由对应的实现类 DefaultSingletonBeanRegistry 负责三级缓存的具体实现(存储 Bean 最终是通过三个 Map 对象);
  3. AbstractBeanFactory 是 BeanFactory 接口的实现类,定义了createBean()方法,AbstractBeanFactory 将getSingleton()函数整合到getBean函数中,定义了 Bean 是从 beanMap 容器中获取还是单独初始化 Bean 对象;
  4. AbstractAutowireCapableBeanFactory 类是createBean的具体实现,它才是 Bean 创建的核心;此对象中整合了 Bean 的创建、装配、初始化、销毁等操作
  5. BeanDefinition 描述了一个 Bean 的元数据信息,包含 bean 的类型、属性值、依赖关系等;初始化一个 Bean 必须要拿到对应的 BeanDefinition 对象;
  6. BeanDefinitionRegistry 定义了管理 BeanDefinition 对象的接口;其中加载一个 Bean 有很多方式如:XML 文件、Java 代码、注解等方式,因此 BeanDefinitionRegistry 接口有很多具体的实现类,针对了不同的 BeanDefinition 加载方式;

面试:Bean 生命周期

  1. 实例化:根据 Java 代码或者 XML 配置等方式加载 BeanDefinition 对象,获取 Bean 元数据信息,然后通过动态代理技术创建 Bean 实例对象;
  2. 属性赋值:根据 Bean 的配置信息给 Bean 实例设置属性值;
  3. Aware 接口回调:Aware 接口让 Spring 有感知能力,可以让外部类拿到 Spring 上下文对象,并对实例化后的 Bean 进行自定义操作;
  4. BeanPostProcessor 前置后置处理器:提供了接口可以在 Bean 实例化前后影响 Bean 实例化结果;
  5. 初始化:针对上面获取的信息和函数对 Bean 进行具体的初始化操作;
  6. 使用 Bean:Bean 初始化完成后可以被其他对象使用;
  7. 销毁:Spring 容器关闭时会调用 destory()函数执行销毁操作;

面试:BeanFactory 和 FactoryBean 区别

BeanFactory 是 Spring 容器管理的接口

BeanFactory 接口定义了管理和维护所有 Bean 对象,它是 Bean 创建的核心,定义了getBean()函数,包含如何获取 Bean 对象,以及和 Bean 的三级缓存紧密相关

FactoryBean 是一个特殊的 Bean

FactoryBean 是一个特殊的工厂类,实现 FactoryBean 接口可以实现自己管理 Bean 的实例化和初始化操作;在 Spring 容器启动时会加载这些实现了 FactoryBean 接口的工厂类。

面试:Bean 的三级缓存

Bean 三级缓存的实现定义在 DefaultSingletonBeanRegistry 类中,该类实现了 SingletonBeanRegistry 接口,描述了 Spring 如何获取一个单例 Bean getSingleton()。源码如下:

Spring源码分析—BeanFactory
MIK-SnembD

Spring 存储 Bean 实际上是通过 Map 对象,因为 Bean 实例化本身是比较耗时的过程,于是 Spring 引入了三级缓存来优化 Bean 的创建过程,三级缓存对应了三个 Map 对象:singletonObjects、earlySingletonObjects、singletonFactories。

  • singletonObjects(一层):存放完全初始化后的 Bean 实例;当 Spring 获取 bean 时首先从该容器中查询是否已经被实例化;
  • earlySingletonObjects(二层):存放尚未完成全部初始化的 Bean 实例;当 Bean 完成了属性赋值和 Aware 回调等操作后就放到此对象;需要等待后置处理器完成后续操作;
  • singletonFactories(三层):存放 ObjectFactory 对象,这是解决循环依赖的关键,当 Spring 实例化对象后在属性赋值时发现存在循环依赖,Spring 会先将该 Bean 的代理对象放入 earlySingletonObjects 缓存;使用 singletonFactories 中的 ObjectFactory 接口创建 Bean,并将该状态下的 Bean 放入 singletonFactories 三层缓存中,提前暴露给 IOC(IOC 可以在该缓存中拿到 Bean 实例)

面试:Spring 如何解决循环依赖

循环依赖是指:在 A 对象中依赖了对象 B,在 B 对象中依赖了 A;在没有三级缓存的情况下,A 对象实例化后在进行属性赋值时发现依赖了对象 B,但是 B 还没有实例化,于是实例化 B,B 实例化后属性赋值时发现依赖 A,但是 A 并没有彻底完成初始化操作(即在 singletonObjects 一层缓存中没有 A),于是 A 和 B 的初始化就陷入循环中。

public class A {
    @Autowired
    private B b;
}

public class B {
    @Autowired
    private A a;
}

解决单例 Bean 循环依赖的分析:

  1. 创建对象 A,完成实例化操作(bean 生命周期第一步),在完成初始化属性赋值(bean 生命周期第二步)时,发现依赖了对象 B,此时 Spring 会先将 beanA 放到三级缓存 singletonFactories 中
  2. 接着去创建对象 B,同样先完成 B 的实例化操作,然后发现依赖对象 A,则开始从缓存 beanMap 中获取对象 A 的实例,这时候虽然 singletonObjects 和 earlySingletonObjects 拿不到对象 A,但是可以在三级 singletonFactories 拿到对象 A(提前暴露),因此 B 可以继续完成初始化操作;
  3. 最后 A 继续完成初始化操作,此时 B 已经完成了初始化,A 在 singletonObjects 缓存中可以直接拿到 B 实例,也就完成了 A 对象的初始化。

源码分析

从上面我们知道 BeanFactory 的getBean()具体实现是AbstractBeanFactory,而创建 Bean 对象的核心是AbstractAutowireCapableBeanFactory类的createBean()函数。

看 AbstractAutowireCapableBeanFactory 类的createBean()函数源码:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
  ....
    beanInstance = this.doCreateBean(beanName, mbdToUse, args);
    ....
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
  ....
  instanceWrapper = this.createBeanInstance(beanName, mbd, args);
  ....
  this.addSingletonFactory(beanName, () -> {
      return this.getEarlyBeanReference(beanName, mbd, bean);
    });
  ....
}

从上面看到在createBeanInstance()之后,会立刻调用addSingletonFactory()方法保存 ObjectFactory 对象,此方法定义在 DefaultSingletonBeanRegistry 类中,看对应源码:

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  Assert.notNull(singletonFactory, "Singleton factory must not be null");
  synchronized(this.singletonObjects) {
    if (!this.singletonObjects.containsKey(beanName)) {
      this.singletonFactories.put(beanName, singletonFactory);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
    }

  }
}

从上面可以看到,在createBeanInstance()之后,Spring 会将 ObjectFactory 对象放入到singletonFactories三层缓存中。

那么到此为止,已经看到 Bean 实例化后会提前暴露到第三层缓存中,也就是提前暴露给 IOC 容器,如此就解决了循环依赖问题。

为什么不能解决多例 bean 的循环依赖

因为单例的实现是依靠 beanMap 缓存,Spring 在创建对象时会优先从缓存中获取而不是创建新的对象;但是多例 bean 会每次都 getBean 创建新的对象,并不会写入到缓存中,Spring 也就无法解决此状态下的循环依赖问题。

Spring 源码专栏

此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题

配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs

联系我

  • 个人博客:http://tycoding.cn/
  • GitHub:https://github.com/tycoding
  • 微信公众号:程序员涂陌
  • QQ 交流群:866685601


原文始发于微信公众号(程序员涂陌):Spring源码分析—BeanFactory

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145636.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!