0
点赞
收藏
分享

微信扫一扫

Spring循环依赖解决思路

DYBOY 2022-02-24 阅读 82

目录

jvm类加载循环依赖

一个Java对象创建分为实例化和初始化两步,例如Person person = new Person();可以拆解为两步:
第一步:Person person;//类Person的引用,存储在栈中,用来指向Person在堆中创建的对象,可以理解为Person对象在堆中描述符,会触发Person类加载和类变量的初始化
第二步:person = new Person();//类Person的一个对象,储存在堆中,会触发类实例变量的默认值的初始化。

jvm类加载循环依赖两种情况:

情况1:

public class ClassAA {
private ClassBB classBB;
public ClassAA(ClassBB classBB ){
this.classBB = classBB;
}
}
public class ClassBB {
private ClassAA classAA;
public ClassBB(ClassAA classAA){
this.classAA = classAA;
}
}
测试代码:
ClassAA classAA = new ClassAA(new ClassBB());//new ClassBB();也需要有参
ClassBB classBB = new ClassBB(new classAA());//new ClassAA();也需要有参

运行结果:编译就不通过,这种写法本身语法逻辑就行不通

情况2:

public class ClassBB {
    private ClassAA classAA;
    public ClassAA getClassAA() {
        return classAA;
    }
    public void setClassAA(ClassAA classAA) {
        this.classAA = classAA;
    }
}

public class ClassAA {
    private ClassBB classBB;
    public ClassBB getClassBB() {
        return classBB;
    }
    public void setClassBB(ClassBB classBB) {
        this.classBB = classBB;
    }
}
测试代码:
ClassAA classAA = new ClassAA();
ClassBB classBB = new ClassBB();
System.out.println(classAA+ ","+ classBB.getClassAA());
System.out.println(classBB +","+ classAA.getClassBB());

运行结果:
在这里插入图片描述
类在循环加载如何解决,todo…

Spring的IoC容器如何解决循环依赖

问题引出

Spring的IoC容器在按照饿汉方式(单例模式)初始化容器的时候,也会同时创建bean,将bean属性初始化,如果beanA其中一个属性是BeanB,beanB的一种属性是BeanA,就形成了Bean对象初始化时的循环依赖。

public class ClassA {
    private ClassB classB;

    public void setClassB(ClassB classB) {
        this.classB = classB;
    }

    public ClassB getClassB() {
        return classB;
    }
}
public class ClassB {
    private ClassA classA;
    public void setClassA(ClassA classA) {
        this.classA = classA;
    }

    public ClassA getClassA() {
        return classA;
    }
}
xml配置文件
<bean id="classA" class="com.noodles.domain.ClassA">
        <property name="classB" ref="classB"></property>

    </bean>
    <bean id ="classB" class="com.noodles.domain.ClassB">
        <property name="classA" ref="classA"></property>
    </bean>
测试代码
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:beans.xml");
ClassA classA = (ClassA) applicationContext.getBean("classA");
ClassB classB = (ClassB) applicationContext.getBean("classB");
System.out.println(classA==classB.getClassA());
System.out.println(classB==classA.getClassB());

程序执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aZudfA4E-1645635316079)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9506ab27-facf-4de8-a54b-fbc4f7f9bf5b/Untitled.png)]

程序正常执行,说明spring在向IOC中注册bean的时候,解决了循环依赖问题。

Spring循环依赖解决办法

bean初始化过程:首先根据解析bean配置文件,从上到下创建bean对象,当bean中的属性是IOC中其他Bean对象是,会先去创建依赖的bean对象,才能完成当前bean的属性初始化。

bean创建的核心逻辑在DefaultSinggletonBeanRegistry和AbstranctBeanFactory类中,两个类属于继承关系。

,color_FFFFFF,t_70,g_se,x_16)

在从IoC容器中获取bean时,依次从一级缓存singletonObjects-》二级缓存earlySingletonObjects-》三级缓存中singletonFactories根据bean id判断bean对象是否存在。

三级缓存作用:

一级缓存:Map<String,String> singletonObjects,key时bean的id,value时初始化的bean对象,也是最终可以直接拿来用的bean对象。

二级缓存:Map<String,Obejct> earlySingletonObjects ,key时bean的id,value时实例化好的对象,还没有给对象属性初始化。

三级缓存:Map<String,ObjectFactory<?>> singletonFactories,key时bean的id,value时类型时ObjectFactory,ObjectFactoy时一个函数式接口,其中里面的getObject方法用来被重写生成bean对象,value可以理解为一段生成bean对象的逻辑。

在这里插入图片描述
解决循环依赖是否需要三级缓存,首先尝试使用两级缓存看能不能解决循环依赖问题,此时一个bean对象创建过程:

getBean-》实例化-》填充属性-》初始化

在这里插入图片描述
Spring通过AOP,有时需要对一些对象进行动态代理,然后将动态代理对象放在IoC容器中,要同时解决循环依赖,需要用到三级缓存,此时创建一个bean的过程变成:
getBean-》实例化-》填充属性-》初始化-》生成代理对象
在这里插入图片描述

总结

当没有循环依赖式,使用一级缓存就可以。当有循环依赖,不需要对对象进行动态代理时,两层缓存就可以解决;当对象需要动态代理,并且代理对象需要存在循环依赖,才需要三级缓存。

举报

相关推荐

0 条评论