栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring的循环依赖问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring的循环依赖问题

问题提出 Spring中代码-产生循环依赖问题
@Component
class A{

    private B b;

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

@Component
class B{

    private A a;

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

报错
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-05-26 12:46:05.173 ERROR 10804 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  a defined in file [C:UserslianleiIdeaProjectsvideowebsitetargetclassescomllvideowebsiteutilsA.class]
↑     ↓
|  b defined in file [C:UserslianleiIdeaProjectsvideowebsitetargetclassescomllvideowebsiteutilsB.class]
└─────┘


Action:

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.

问题:

The dependencies of some of the beans in the application context form a cycle:

循环依赖注入,主要是因为,使用构造方法进行注入,A对象初始化的时候需要用到IOC容器当中的B对象,而B对象同样需要IOC中的A对象,因此报错。但是如果是通过Setter方法注入,就不会产生该问题
原因如下:

在第一步和第二内部,是首先创建实例化对象,然后进行属性注入,如果采用构造方法注入,A、B两个对象都无法成功实例化,但是如果使用的是setter方法,就可以在实例化之后进行属性注入,就不会有问题。如果构造方法和setter方法同时存在,会优先使用构造方法,因此会爆错。

解决方法 1. 懒加载

在其中一个需要加载的地方加上 @Lazy 注解,就可以延迟加载该属性。

@Component
@Component
class A{

    private B b;

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

如上,在private B b;加上 @Lazy ,再初始化A对象的时候,就不会直接注入a,不用去IOC容器当中查找单例对象A,就不会产生循环依赖问题。创建B的时候依然会使用A的构造方法,但是由于使用@Lazy注解,因此Spring会生成一个代理对象返回,使得A可以正常创建。从而解决以来循环问题。
使用@Lazy解决循环依赖问题

2. 二级缓存

在缓存池当中缓存A和B对象,其中属性都是空白的。


缺点:

如果注入的是被AOP管理的对象,那么就需要在单例池当中注入被动态代理的对象,而不是原对象。

怎么办?

三级缓存

三级缓存:如果引用的对象配置了AOP,那在单例池中最终就会需要注入动态代理对象,而不是原对象。而生成动态代理是要在对象初始化完成之后才开始的。于是Spring增加三级缓存,保存所有对象的动态代理配置信息。在发现有循环依赖时,将这个对象的动态代理信息获取出来,提前进行AOP,生成动态代理。

Spring 是如何解决循环依赖问题的

private final Map singletonObjects = new ConcurrentHashMap(256);
 

private final Map> singletonFactories = new HashMap>(16);
 

private final Map earlySingletonObjects = new HashMap(16);

 @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //从一级缓存,也就是单例池当中获取数据
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        //一级缓存没有就从二级缓存拆线呢
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized(this.singletonObjects) {
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                        //二级也没有,说明是需要AOP,因此会从三级缓存加载singletonFactory ,这里面保存的的代理信息,通过代理信息,在二级缓存当中生成代理对象,从而可以解决依赖循环。                            ObjectFactory singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }

        return singletonObject;
    }

注意:

如果不是单例模式,就不会出现循环引用的问题。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/933262.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号