从spring源码层面分析循环依赖解决方案的实现原理(下)

简介: 从spring源码层面分析循环依赖解决方案的实现原理(下)

三级缓存中包含A和B的2个lambda表达式,A和B对象还没有放入缓存中。

从容器中查询a

image.png

一级缓存和二级缓存都没有,三级缓存中虽然没有a对象,但是有ObjectFactory。执行 singletonFactory.getObject()实际上调用的是lambda表达式getEarlyBeanReference(beanName, mbd, bean)。


image.png

如果有代理对象,则返回代理对象,如果没有代理对象,则返回原始对象。

因为xml中并没有给a对象配置aop代理,所以这里返回的是a的原始对象。

image.png

查看getEarlyBeanReference方法可以看到AOP代理的两种实现方式。


image.png

image.png

image.png

为什么删除三级缓存a的objectFactory,因为查找顺序是先查一级缓存,再查二级缓存,最后查三级缓存,二级缓存已经有了a对象,所以三级缓存内容就没有留着的必要了,这也是三级缓存容量比较小的原因。

获取a对象的目的是给b对象中的a属性赋值。


image.png

image.png

image.png

image.png

那么就不需要createBean了,整个逻辑才算结束了。

总结

1、三个map结构中分别存储了什么对象?

一级缓存:成品对象;二级缓存:半成品对象;三级缓存:lambda表达式(getEarlyReference)

2、三个map缓存的查找对象的顺序?

先找一级缓存,查不到,再查二级缓存,再找不到,找三级缓存。

3、如果只有一个map,能否解决循环依赖问题?

不能,如果只有一个map,那么成品对象和半成品对象只能放入一个map中,而半成品对象是不能暴露给外部使用的,所以必须要做区分,否则就有可能暴露半成品对象。

有人可能说添加一个标识,0=半成品、1=成品,每次取的时候都要判断下这个标识别,很麻烦。

4、如果只有两个map,能否解决循环依赖问题?

能,但有前提条件。

修改源码1

把createBean方法中的

this.addSingletonFactory(beanName, () -> {
                return this.getEarlyBeanReference(beanName, mbd, bean);
            });

注释掉,然后添加,earlyearlySingletonObjects.put(beanName,bean)

即不往三级缓存中放入ObjectFactory对应的lambda表达式,而是往二级haunch中放入bean对象。

修改源码2

把getSingleton方法中的

image.png

这一部分,换成

singletonObject=this.earlySingletonObjects.get(beanName);
return singletonObject;

即不从三级缓存中获取,而是直接从二级缓存获取。

运行下发现并没有报错


image.png

再来运行代码,就会报错:other beans do not use the final version of the bean即其他bean不能使用最终版本的bean。

所以只要不包含aop就可以使用二级缓存解决循环依赖问题,但是出现aop之后,就必须要使用三级缓存了。

5、为什么三级缓存就可以解决循环依赖中包含的代理对象问题呢?

(1)创建代理对象的时候是否需要创建出原始对象?

在没有生成代理对象之前就可以生成原始对象了

image.png

image.png

这个方法是创建代理对象的。

(2)同一个容器中能否出现两个不同的对象?不能,对象名都叫a,要么是原始对象要么是代理对象,不能同时出现。

(3)如果一个对象被代理,那么原始对象和和代理对象应该这么去存储?

如果需要代理对象,那么代理对象创建完之后应该覆盖原始对象。

在getEarlyBeanReference方法中,会判断是否需要代理对象,如果创建出了代理对象,就需要覆盖原始对象。

(4)在对象对外暴露的时候,容器怎么知道什么时候需要被暴露呢?或者说在对象对外暴露的时候,如何准确的给出原始对象或代理对象?

因为正常的代理对象的创建是在BeanPostProcessor的后置方法中,在解决循环依赖问题的时候还没有执行到那个地方,所以此时就需要lambda表达式了,类似于一种回调机制,在确定要对外暴露的时候,就唯一性确定到底是代理对象还是原始对象,这也是什么不把对象直接放入二级缓存中,而通过三级缓存lambda表达式的方式来执行的原因。


image.png

addSingletonFactory这里把lambda表达式放入三级缓存,该表达式的内容是判断是否需要代理对象,若需要则创建,这里并没有真正的去执行该表达式。

initializeBean这个方法中的applyBeanPostProcessorsBeforeInitialization方法才是真正的通过后置方法beanPostProcessor创建代理对象,而在处理循环依赖的时候并没有执行到这一步呢。

程序没有办法去确定你写的这个对象什么时候被其他对象调用,什么时候需要变成某一个对象的属性,所以把他换成一个lambda表达式,在确定需要对外暴露的时候才执行对象的确定(原始还是代理)。

先创建a对象,再创建b对象,需要给b中的a属性赋值,从三级缓存中找到lambda表达式,然后判断是否需要代理对象。lambda表达式相当于回调机制,并不会立刻执行,当你需要给这个属性赋值的时候,你才会去执行。

image.png

刚开始是原始对象,如果需要被代理,则返回被代理之后的对象。

某一个对象的代理只能是一个,如果是多个代理的话,就需要看代理对象的创建顺序了。

(5)spring提供了循环依赖的解决方案,那日常工作中是否也会有遇到循环依赖的问题?

spring是一个跟业务无关的技术框架,它只能预防一些问题,而不是解决所有问题,就跟我们写代码的时候的异常处理一样,你能判断到一些问题,但不是所有的异常情况你都能全部解决掉的。


image.png

相关文章
|
9天前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
12天前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
5月前
|
存储 人工智能 自然语言处理
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
2304 116
|
2月前
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
|
2月前
|
监控 架构师 NoSQL
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
364 70
|
4月前
|
前端开发 Java 数据库连接
Spring核心原理剖析与解说
每个部分都是将一种巨大并且复杂的技术理念传达为更易于使用的接口,而这就是Spring的价值所在,它能让你专注于开发你的应用,而不必从头开始设计每一部分。
160 32
|
4月前
|
Java 开发者 Spring
Spring框架 - 深度揭秘Spring框架的基础架构与工作原理
所以,当你进入这个Spring的世界,看似一片混乱,但细看之下,你会发现这里有个牢固的结构支撑,一切皆有可能。不论你要建设的是一座宏大的城堡,还是个小巧的花园,只要你的工具箱里有Spring,你就能轻松搞定。
172 9
|
3月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
428 0

热门文章

最新文章