Spring常考知识点

Posted by lily's blog on October 24, 2024

循环依赖问题

参考文章:

  1. 三级缓存是如何解决bean循环依赖的

bean的循环依赖解决过程

顺序为,实例化、属性赋值(属性填充)、初始化、依赖注入

实例化 Bean: Spring 在实例化 Bean 时,会先创建一个空的 Bean 对象,并将其放入一级缓存中。

属性赋值: Spring 开始对 Bean 进行属性赋值,如果发现循环依赖,会将当前 Bean 对象提前暴露给后续需要依赖的 Bean(通过提前暴露的方式解决循环依赖)

初始化 Bean: 完成属性赋值后,Spring 将 Bean 进行初始化,并将其放入二级缓存中。

注入依赖: Spring 继续对 Bean 进行依赖注入,如果发现循环依赖,会从二级缓存中获取已经完成初始化的 Bean 实例。

三级缓存如何解决循环依赖

Spring可以解决使用setter注入(也就是使用注解注入的方式)的循环依赖问题。 因为spring解决循环依赖本质上是通过bean的不同加载阶段使用的不同缓存来解决的。主要就依靠于singletonFactories这个第三级缓存来解决。 spring是没有办法解决使用构造器注入方式导致的循环依赖问题的,因为bean要加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决。 如果A对象依赖于B对象。第三级缓存是通过A的工厂对象获取到的A的早期引用,将这个早期引用的占位符提前暴露给B对象而解决的循环依赖问题。

bean在加载过程的顺序:

  1. 先从一级缓存singletonObjects中去获取。(如果获取到就直接return)
  2. 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存earlySingletonObjects中获取。(如果获取到就直接return)
  3. 如果还是获取不到,且允许singletonFactories(allowEarlyReference=true)通过getObject()获取。就从三级缓存singletonFactory.getObject()获取。(如果获取到了就从singletonFactories中移除,并且放进earlySingletonObjects。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存)

getSingleton()从缓存里获取单例对象步骤分析可知,Spring解决循环依赖的诀窍:就在于singletonFactories这个第三级缓存。这个Cache里面都是ObjectFactory,它是解决问题的关键。

通过这样的三级缓存延迟初始化机制可以解决我们的循环以来问题。

三级缓存

三级缓存的

一级缓存

  • 数据结构Map<String, Object>,键为bean的名称,值为已初始化的bean实例。
  • 描述:存储完全初始化好的bean,可以直接使用。
  • 位置DefaultSingletonBeanRegistry 类中的 singletonObjects 属性。

二级缓存

  • 数据结构Map<String, Object>,键为bean的名称,值为早期的bean引用。
  • 描述:存储已实例化但未完全初始化的bean,主要用于解决循环依赖。
  • 位置DefaultSingletonBeanRegistry 类中的 earlySingletonObjects 属性。

三级缓存

  • 数据结构Map<String, ObjectFactory<?>>,键为bean的名称,值为生成早期bean引用的工厂对象。
  • 描述:存储ObjectFactory对象,用于在创建bean时解决循环依赖。
  • 位置DefaultSingletonBeanRegistry 类中的 singletonFactories 属性。

存储一个ObjectFactorty对象,用于创建并返回需要获取对象的早期地址。

  • 三级缓存中的早期引用确实是指向正在创建的 A 的内存地址,但它是一个不完整的占位符。
  • 一旦 A 完全初始化,后续的依赖将会使用 A 的实际引用,而不是早期引用。这样确保了在循环依赖的情况下,所有依赖关系能够正确解决。

Spring中使用的设计模式

  1. 单例模式(Singleton Pattern)
    • 确保一个类只有一个实例,并提供全局访问点。Spring的bean默认是单例的。
  2. 工厂模式(Factory Pattern)
    • 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Spring通过ObjectFactory实现了这一模式。
  3. 代理模式(Proxy Pattern)
    • 为其他对象提供一种代理以控制对这个对象的访问。Spring AOP使用代理模式来实现切面编程。
  4. 模板方法模式(Template Method Pattern)
    • 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。Spring的JdbcTemplate就是这种模式的一个例子。
  5. 观察者模式(Observer Pattern)
    • 定义了一种一对多的依赖关系,使得当一个对象改变状态时,所有依赖于它的对象都得到通知并自动更新。Spring的事件机制利用了这一模式。

spring中事务失效

场景

解决