spring解决循环依赖的方案示例

 更新时间:2023年05月30日 11:07:21   作者:一叶飘零晋  
这篇文章主要介绍spring如何解决循环依赖,文中有相关的代码示例给大家参考,对我们的学习或工作有一定的帮助,感兴趣的同学可以借鉴阅读

一、代码

@Component
public class BService {
    @Autowired
    private AService aService;
    public void work(){
        System.out.println("bservice的工作");
    }
}
@Component
public class AService {
    @Autowired
    private  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代理问题。

以上就是spring解决循环依赖的方案示例的详细内容,更多关于spring解决循环依赖的资料请关注脚本之家其它相关文章!

相关文章

  • Springboot过滤器禁止ip频繁访问功能实现

    Springboot过滤器禁止ip频繁访问功能实现

    这篇文章主要介绍了Springboot过滤器禁止ip频繁访问功能实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • SpringSecurity实现踢出指定用户的示例

    SpringSecurity实现踢出指定用户的示例

    SpringSecurity中使用SessionRegistryImpl类可以获取session信息并踢出用户,这篇文章主要介绍了SpringSecurity实现踢出指定用户的示例,需要的朋友可以参考下
    2025-03-03
  • 搜索一文入门ElasticSearch(节点 分片 CRUD 倒排索引 分词)

    搜索一文入门ElasticSearch(节点 分片 CRUD 倒排索引 分词)

    这篇文章主要为大家介绍了搜索一文入门ElasticSearch(节点 分片 CRUD 倒排索引 分词)的基础详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • Java Validation方法入参校验实现过程解析

    Java Validation方法入参校验实现过程解析

    这篇文章主要介绍了Java Validation方法入参校验实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • JAVA 数据结构之Queue处理实例代码

    JAVA 数据结构之Queue处理实例代码

    这篇文章主要介绍了JAVA 数据结构之Queue处理实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • Java自带消息队列Queue的使用教程详细讲解

    Java自带消息队列Queue的使用教程详细讲解

    这篇文章主要介绍了Java自带消息队列Queue的使用教程,Java中的queue类是队列数据结构管理类,在它里边的元素可以按照添加它们的相同顺序被移除,队列通常以FIFO的方式排序各个元素,感兴趣想要详细了解可以参考下文
    2023-05-05
  • Java比较集合是否相等的技巧分享

    Java比较集合是否相等的技巧分享

    集合使我们开发过程中非常用到的,要说最常用的,非List莫属,那么当我们想要比较两个List是否相等的时候,应该怎么处理呢?本文将介绍几个小技巧来处理,需要的朋友可以参考下
    2025-06-06
  • Java中数据库连接池HikariCP和Druid的技术对比与性能分析

    Java中数据库连接池HikariCP和Druid的技术对比与性能分析

    在现代Java应用开发中,数据库连接池是提升应用性能的关键组件之一,本文将深入对比两个主流的Java数据库连接池,希望对大家有一定的帮助
    2025-08-08
  • 解决SpringMVC拦截器path路径的坑

    解决SpringMVC拦截器path路径的坑

    这篇文章主要介绍了解决SpringMVC拦截器path路径的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • 解决Test类中不能使用Autowired注入bean的问题

    解决Test类中不能使用Autowired注入bean的问题

    这篇文章主要介绍了解决Test类中不能使用Autowired注入bean的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论