Spring 循环依赖难点分析

目录

循环依赖和三级依赖

Spring 三级缓存和循环依赖(紧密相关)

循环依赖
  • 指的是两个或多个Bean之间相互依赖,如Bean A依赖于Bean B,而Bean B又依赖于Bean A,则会出现循环依赖的情况
  • Spring默认采用Singleton模式,Bean默认是单例的,在容器启动时就会创建Bean实例,同时也会注入它所依赖的Bean实例
  • 当出现循环依赖的情况时,Spring的Bean实例化策略就会出现问题
  • 大家开发过程中好像对循环依赖这个概念无感知,这种错觉是因为工作在Spring的中,已经帮你解决了
解决方案
  • Spring引入了"提前暴露Bean"的机制,在创建A对象时,会先创建一个A的空对象并将其添加到缓存池中
  • 即"提前暴露Bean",然后继续创建B对象,将其注入A对象中
  • 在创建B对象时,由于A对象已经在缓存池中,可以直接获取到A对象,接着将B对象注入到A对象中,完成Bean的初始化
  • 注意:循环依赖的场景
    • 单例
      • 构造器循环依赖(构造器注入,循环依赖无法解决,日常开发不推荐构造器注入)
      • Field属性循环依赖(set方法注入,循环依赖问题可以解决,通过三级缓存)
    • 多例
      • prototype类型bean(循环依赖无法解决,构造函数和属性注入都不能解决循环依赖)
      • AbstractBeanFactory#doGetBean
缓存说明
  • 三级缓存,当Spring创建Bean的过程中,使用3个缓存存储已经创建的Bean实例,Map结构
    • 一级缓存(成熟的bean) Singletons object cache
      • singletonObjects 单例池 ,存储已经创建好的无代理的单例Bean对象,从该缓存中取出的 bean 可以直接使用
    • 二级缓存 Early-singletons object cache
      • earlySingletonObjects 提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依
    • 三级缓存 Singleton Factory object cache
      • singletonFactories 单例对象工厂的cache,存放 bean 工厂对象,解决循环依赖 避免Bean的实例化顺序出现问题
总结
  • Spring的三级缓存机制是为了避免Spring中的循环依赖问题而引入的
  • 也可以提高Bean的创建效率,避免创建重复的对象,从而提高应用程序的性能

三级缓存流程解析

Bean的创建流程

循环依赖代码案例

java 复制代码
@Service
public class OneServiceImpl implements OneService {
    @Autowired
    private TwoService twoService;
    ...
}
java 复制代码
@Service
public class TwoServiceImpl implements TwoService {
    @Autowired
    private OneService oneService;
    ...
}

关键类 DefaultSingletonBeanRegistry

bash 复制代码
  /**
   * 一级缓存,单例池 ,存储已经创建好的无代理的单例Bean对象,从该缓存中取出的 bean 可以直接使用
   * */
 Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

  /**
   * 三级缓存, 单例对象工厂的cache,存放 bean 工厂对象,解决循环依赖 避免Bean的实例化顺序出现问题。
   * */
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

  /**
   * 二级缓存, 提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依
   * */
Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

  /** 用于保存已经创建的 Bean 的单例对象,能够确保应用程序上下文中每个单例 Bean 对象最多只创建一次,并且可以保证单例 Bean 的唯一性 */
Set<String> registeredSingletons = new LinkedHashSet<>(256);

  /** 表示bean创建过程中都会存储这里,创建完成后会被移除 */
Set<String> singletonsCurrentlyInCreation =  Collections.newSetFromMap(new ConcurrentHashMap<>(16));


//add 关键方法 
//把创建的bean加到一级缓存中,然后把二级和三级缓存的bean移除
addSingleton(String beanName, Object singletonObject)

//三级缓存中存储,二级缓存中移除bean ,ObjectFactory是函数式接口
addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)

//get关键方法
//根据bean名称获取对应的bean,如果不存在则创建新的,ObjectFactory是调用外部传递进来的lambda表达式
getSingleton(String beanName, ObjectFactory<?> singletonFactory)

//用于从单例池中获取指定名称的单例 Bean 实例,方法内部实现了三级缓存查找机制
getSingleton(String beanName, boolean allowEarlyReference)

关键类 ObjectFactory

bash 复制代码
/**
 * 将创建对象的步骤封装到ObjectFactory中, 外部通过lambda表达式把相关创建步骤传递进来,返回创建好的对象
 /
@FunctionalInterface
public interface ObjectFactory<T> {
  T getObject() throws BeansException;
}

循环依赖流程图

java 复制代码
// 1、bean构建完毕,从singletonsCurrentlyIncreation集合中删除对应的beanName
afterSingletonCreation(beanName);
 
// 2、添加到单例缓存池中,并从二级缓存和三级缓存中删除
addSingleton(beanName, singletonObject);
java 复制代码
	// 添加到单例缓存池中,并从二级缓存和三级缓存中删除
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

补充-Spring循环依赖异常

相关推荐
卡卡酷卡BUG16 小时前
Java 后端面试干货:四大核心模块高频考点深度解析
java·开发语言·面试
Yolo566Q16 小时前
OpenLCA生命周期评估模型构建与分析
java·开发语言·人工智能
brzhang17 小时前
读懂 MiniMax Agent 的设计逻辑,然后我复刻了一个MiniMax Agent
前端·后端·架构
lang2015092817 小时前
Spring Boot日志配置完全指南
java·spring boot·单元测试
在坚持一下我可没意见17 小时前
HTTP 协议基本格式与 Fiddler 抓包工具实战指南
java·开发语言·网络协议·tcp/ip·http·java-ee·fiddler
草明17 小时前
Go 的 IO 多路复用
开发语言·后端·golang
蓝-萧17 小时前
Plugin ‘mysql_native_password‘ is not loaded`
java·后端
故事不长丨17 小时前
【Java SpringBoot+Vue 实现视频文件上传与存储】
java·javascript·spring boot·vscode·后端·vue·intellij-idea
不修×蝙蝠18 小时前
Java 日志演进:一文读懂主流框架
java·log4j·logback·log4j2·日志·slf4j
9ilk18 小时前
【仿RabbitMQ的发布订阅式消息队列】--- 前置技术
分布式·后端·中间件·rabbitmq