java多线程    Java入门    vsftp    ftp    linux配置    centos    FRP教程    HBase    Html5缓存    webp    zabbix    分布式    neo4j图数据库    

Spring循环依赖如何解决的

一、什么是循环依赖?

一个两个以上的bean互相持有对方,比如A持有B B持有A.

Spring 注入bean 的方式有 field 属性注入 ,构造器注入

二、造成循环依赖的代码1

field属性注入-这个例子会正确执行

@Service
public class A {
    @Autowired
    private B b;
}

@Service
public class B {
    @Autowired
    private A a;
}

原理

Spring 在创建 bean 的时候并不是等它完全完成,而是将创建中的 bean 提前曝光(即加入到 singletonFactories 三级缓存中),当下一个 bean 创建的时候需要依赖此 bean ,则从三级缓存中获取。

Spring创建一个Bean的时候,不是完全完成了,而是将这个bean提前加入singletonFactories三级缓存,下一个bean创建以来这个Bean的时候,可以从三级缓存获取。

  1. 先创建 A 对象,并将创建出来的 A 对象放到 Spring 的三级缓存中;
  2. Spring的三级缓存解释
  3. 此时 A 对象的 b 属性为空,现在A需要填充 b 属性,到缓存中查询 B 对象,没有查到,触发 B 对象的创建流程;
  4. 创建 B 对象,并将创建出来的 B 对象放到 Spring 的三级缓存中;
  5. B 对象的 a 属性为空,需要填充 a 属性,随后在缓存中找到 A 对象,完成填充注入;
  6. 最后再对 A 对象的 b 属性进行填充,从缓存中顺利拿到 B 对象,完成属性注入,循环依赖到此解决。

Spring的三级缓存解释

  • singletonObjects:一级缓存,用于存放完整的 bean,从该缓存中取出的 bean 可以直接使用;
  • earlySingletonObjects:二级缓存,存放提前暴露的 bean,bean 是不完整的,未完成属性注入和执行 init 方法,用于解决循环依赖;
  • singletonFactories:三级缓存,对初始化后的 bean 完成 AOP 代理操作,bean 初始化完成之后才生成代理,而不是实例化之后就生成代理,保证了bean的生命周期。

singleton: 单身的,单独的

三、造成循环依赖的代码2

构造器注入例子--这个例子将报错

@Service
public class A {
    private B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }

}

@Service
public class B {
    private A a;

    @Autowired
    public B(A a) {
        this.a = a;
    }

}

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

解决方案1.CGLIB 动态代理

@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) // 使用 CGLIB 动态代理
@Service
public class B {

    private A a;

    @Autowired
    public B(A a) {
        this.a = a;
    }

}

@Scope 注解是 Spring IOC 容器中的一个作用域,默认为 singleton(单例)。通过 Scope 中的 proxyMode 属性可以设置类的代理模式,

DEFAULT 不使用代理
NO 不使用代理
INTERFACES 使用 JDK 动态代理
TARGET_CLASS 使用 CGLIB 动态代理

这里我们不对 @Scope 做过多介绍,上边代码中因为 B 类没有实现接口,不能使用 JDK 动态代理,所以这里使用的是 CGLIB 动态代理。

解决方案2.Lazy

    @Autowired
    public B(@Lazy A a) {
        this.a = a;
    }

这两种方式又是如何解决循环依赖问题的呢?

其实它们 都是通过动态代理来避免了循环依赖。

我们再来描述一下 解决循环依赖的过程:

P1. 创建 A 时需要 B ,发现 B 是可以延迟加载或者是指定了代理模式的;
P2. 创建一个 B 类的代理类 Bproxy ;
P3. 通过 Bproxy 创建 A(此时 A 的依赖关系已经变成了 A 依赖 Bproxy);
P4. 再创建 B 时,A 已经存在,所以 B 也成功创建。

三种Spring Autowired比较【https://www.cnblogs.com/mili0601/p/15582421.html

https://developer.aliyun.com/article/766880

https://blog.csdn.net/daidaineteasy/article/details/115559725

延伸问题

1.什么是AOP

2.什么是Bean

3.什么叫反射 https://blog.csdn.net/qq_51515673/article/details/124830558


This entry was posted in JAVA. Bookmark the permalink.
月小升QQ 2651044202, 技术交流QQ群 178491360
首发地址:月小升博客https://java-er.com/blog/spring-loop-how-to/
无特殊说明,文章均为月小升原创,欢迎转载,转载请注明本文地址,谢谢
您的评论是我写作的动力.

Leave a Reply