自学内容网 自学内容网

Spring 循环依赖会出现什么情况? 如何解决?

Spring 循环依赖会出现什么情况? 如何解决?

一、什么是循环依赖?

在Spring项目中我们经常使用 @Autowired或者@Resource去注入Bean,我们称之为依赖。
当多个Bean之间存在互相依赖的关系,并且出现了循环调用时,Spring就会找不到依赖的七点,就会死循环直到抛出异常。
例如:A依赖B,B依赖C,C依赖A,三者必须在依赖的类初始化之后才会初始化自己,从而出现死循环。

二、实战场景

笔者是在使用Spring Security 编写登录和权限验证代码时出现了循环依赖的场景。

三、解决方法

省流: 笔者使用的方法

  1. 使用@Lazy延迟创建对象
  2. 将@Resource替换为@Autowired
  3. 新建一个空Bean,来解决依赖问题: 例如 A依赖B,B依赖A,我们可以新建一个C,然后让A依赖C,B实现C即可将直接依赖转化为间接依赖关系 中介方式打破循环链
     
    public interface C {
        void doSomething();
    }
    
    
    public class B implements C {
        @Override
        public void doSomething() {
            
        }
    }
    
    
    public class A {
        private C c;
    
        public A(C c) {
            this.c = c;
        }
    
        public void doSomething() {
            c.doSomething();
        }
    }

四、扩展:@Autowired是如何解决循环依赖的的问题的

解决的核心是使用了Spring的三级缓存:

  • 第一级缓存:singletonObjects,用于存放完全初始化好的bean,避免重复创建,单例池
  • 二级缓存:earlySingletonObjects 存放原始的bean对象,尚未填充属性,同时也没有进行完成依赖注入的类 (核心)
  • 三级缓存:singletonFactories 用于存放bean工厂对象中的getObject方法,用于产生原始的bean或者代理对象(如果Bean被AOP切面代理)来放入二级缓存
    首先我们要知道,实例化 ≠ 完全初始化,当Spring容器创建bean时,会从一级缓存中寻找,如果没找到,会搜索二级缓存,如果存在就会把它注入,如果没有会找三级缓存。当bean初始化时,如果发现依赖的类没有完成完全初始化,就会先使用二级缓存中的bean实例,当所有的bean都初始化之后再从一级缓存中获取完全初始化的bean
    而我们使用的@Resource并不存在这种机制,会直接抛出BeanCurrentlyInCreationException
    只用两级缓存可以吗?
    如果没有AOP的情况下只是用一级和三级缓存就能解决,但是涉及到AOP时,必须使用了
    如果发生循环依赖的话,就去 三级缓存 singletonFactories 中拿到三级缓存中存储的 ObjectFactory 并调用它的 getObject() 方法来获取这个循环依赖对象的前期暴露对象(虽然还没初始化完成,但是可以拿到该对象在堆中的存储地址了),并且将这个前期暴露对象放到二级缓存中,这样在循环依赖时,就不会重复初始化了!


原文地址:https://blog.csdn.net/m0_74047598/article/details/142690004

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!