spring如何解决循环依赖

news/2024/10/23 5:49:52/

一、代码

@Component
public class BService {@Autowiredprivate AService aService;public void work(){System.out.println("bservice的工作");}
}
@Component
public class AService {@Autowiredprivate  BService bService;public void work(){System.out.println("aservice的工作");}
}

二、AService 的bean创建过程

  • spring调用AService的无参构造方法实例化得到AService类得一个aService对象。
  • spring通过依赖注入填充aService中的bservice属性。(先从单例池去找如果没有就创建BService
    一直循环下去。。。。。
  • 填充其他属性
  • 其他步骤
  • 加入单例池

所以产生循环依赖。

三、spring三级缓存

Spring三级缓存指的是Spring框架在管理Bean时所维护的三级缓存机制,其作用是提高Bean的创建效率和管理效率。

1、singletonObjects:该缓存中缓存的是完全创建好的单例Bean,即在第二级缓存(factoryBeanInstanceCache)中返回了完整的Bean实例。

2、earlySingletonObjects:该缓存中缓存的是未完全创建好的单例Bean实例(即只完成了实例化和初始化部分),主要为了解决循环依赖问题,即当一个Bean A依赖于Bean B,而Bean B又依赖于Bean A时,通过从earlySingletonObjects缓存中获取到还未完成创建的Bean A实例,并将其注入到Bean B中,使得依赖注入操作可以顺利完成。

3、singletonFactories:该缓存中缓存的是创建Bean的工厂,即BeanFactory的getObject()方法返回的Bean,也即Bean的创建过程。主要针对的是动态代理类型的对象。

四、三级缓存的使用过程

1、获取singletonObjects缓存中的Bean实例,如果存在则直接返回Bean对象,否则继续操作;
2、获取earlySingletonObjects缓存中的Bean实例,如果存在则返回Bean对象,否则继续操作。
3、获取singletonFactories缓存中的Bean实例(即Bean的创建工厂),如果存在则通过工厂方法创建Bean实例并保存到earlySingletonObjects缓存中、从singletonFactories缓存中移除并返回Bean实例,否则继续操作。

当一个Bean被创建完成并添加到singletonObjects缓存中后,其它依赖该Bean的Bean便可以通过getBean()方法直接获取到完整的Bean实例,并完成依赖注入操作。

五、解决循环依赖

在这里插入图片描述

注意:
spring的依赖注入方式。分为setter注入和构造器注入。spring可以解决setter类型的构造注入,构造器形式的注入解决不掉。
spring的生命周期可以概括为四个大阶段,实例化,属性赋值,初始化,销毁。

五、如果只有一级缓存能否解决依赖的问题

理论上可以,但是实际操作的时候会有问题,一级缓存和二级缓存的区分点,一个存放的是成品对象,一个存放的是半成品对象,当只有一个map的时候就意味着半成品对象和成品对象放到一起,半成品对象不能够直接暴露给外部使用,因为会有空指针异常,所以如果非要用一个map存储就要添加一个标识,来标注是半成品对象还是成品对象。如果按照这样方式设计代码,会很不优雅,所以可以直接用两个map来解决.不需要一个。

六、如果只有二级级缓存能否解决依赖的问题。

理论上可以,但是前提是在创建对象中不能有代理对象。

七、为什么必须要有三级缓存来解决循环依赖问题?为什么三级缓存可以解决带有代理对象的循环依赖问题

1、同一个容器中能否出现同名的不同对象。

不能

2、如果出现了同名的不同对象,应该怎么办。 比如刚开始创建出原始对象,后续创建出了代理对象。

如果在创建过程中出现了同名的不同对象,那么后面创建的对象会覆盖前面所创建的对象。

3、为什么要使用lambda表达式这样的方式,或者为什么要加入三级缓存呢?
对象的属性的赋值是在 populateBean方法完成的

代理对象的创建是在BeanPostProcessor的后置处理方法里面完成的。

public interface BeanPostProcessor {// 注意这个方法名称关键的是before这个单词Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;// 注意这个方法名称关键的是after这个单词Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

populateBean()要比BeanPostProcessor的后置方法先执行也就是说

在进行对象属性赋值的时候,代理对象还没有创建出来,那么属性的赋值只能是原始对象而在后续的步骤中又创建出了代理对象,此时的代理对象会有赋值的过程吗?不会,所以会出现一个错误
this means that said other beans do not use the final version of the bean
就是说赋值是原始对象,而最终留下来的是代理对象,所以导致没有使用最终版本的bean对象。

如何解决?
将代理对象的创建过程提前执行,也就是说在进行对象赋值的时候必须要唯一性的确定出到底是原始对象还是代理对象,这个方法是在getEarlyBeanReference方法里执行的,而getEarlyBeanReference是在populateBean方法的中调用的。

为什么使用lambda表达式

lambda相当于延迟执行,因为此方法次方法并不会在方法的调用的时候立即执行,而是在对象必须要进行属性赋值的那一刻执行,也就是说在对象赋值的的那一刻确定出了最终的bean对象。

总结:使用三级缓存本质上是为了解决aop代理问题。当一个对象需要被代理的时候,在整个整个bean创建过程中,包含两个对象,一个是普通对象,一个是代理生成代理对象,bean默认都是单例的,那么在整个过程中三级缓存在getEarlyBeanReference进行了一个判断。如果不需要代理直接放回普通对象,如果需要代理就用代理对象替换。保证了bean的全局唯一性。所以能够解决aop代理问题。


http://www.ppmy.cn/news/102734.html

相关文章

SpringBoot 使用事务报错:No transaction aspect-managed TransactionStatus in scope

当使用Spring Boot进行开发时,你可能会遇到以下错误之一:“No transaction aspect-managed TransactionStatus in scope”。这个错误通常发生在方法中手动回滚事务的情况下,但方法本身没有被Transactional注解修饰。 在本文中,我…

曾经由盛转衰的骈文,却引领后人在文质兼美的创作之路上坚定前行

又叫骈体文,是和散文相对应的一种文体,它兴起于汉末,形成于魏晋,最盛行于南北朝,在初唐、中唐、唐末、五代、宋初时也盛极一时。古人语:两马并驾为骈,所以骈文最大的特点是用对偶的手法&#xf…

Maven——SDK中的构建范围,构建插件,构建参数说明

Maven 依赖使用注意 封装 Maven SDK 的 Dependency 时,需要注意以下几点: 版本控制:确保所依赖的 SDK 版本与当前应用程序的其他依赖项兼容,并在 pom.xml 文件中指定正确的版本号。 稳定性:使用经过稳定测试和验证的…

06. 数据结构之散列表

前言 散列表也叫作哈希表(hash table),这种数据结构提供了键(Key)和值(Value)的映射关系。只要给出一个Key,就可以高效查找到它所匹配的Value,时间复杂度接近于O(1) 1.…

哪些因素会成为系统的瓶颈

CPU 如果存在大量的计算,他们会长时间不间断的占用 CPU 资源,导致其他资源无法争夺到 CPU 而响应缓慢,从而带来系统性能问题,例如频繁的 FullGC,以及多线程造成的上下文频繁的切换,都会导致 CPU 繁忙&…

从C#学习改善Java编码规范

最近在学习C#,感觉有些编码规范可以引入到Java项目中。 接口 C#中,Interface以大写字母I开头。例如,一个写数据的接口,命名为 IWriter。 Java中,则通常直接命名为Writer。 命名为IWriter的优势在于:一眼…

2023年下半年软考高级需要报班吗?

首先,对于软考高级考试报班与否的问题,需要根据自身的情况来做出决定。如果你有较强的自学能力,且具备丰富的实际工作经验和技术知识,那么不报班也完全可以自学备考。但如果你对软件工程的知识掌握程度较低,或者时间紧…

c语言全局变量和局部变量问题汇总

✅作者简介:嵌入式领域优质创作者,博客专家 ✨个人主页:咸鱼弟 🔥系列专栏:单片机设计专栏 📃推荐一款求职面试、刷题神器👉注册免费刷题 1、关键字static的作用是什么? 定义静态变…