spring 的循环依赖
晚上好,我是卢卡,对于很多新手,项目中刚开始使用的框架就是 spring,但是 spring 你到底了解多少,对于数据操作的底层对象调用,如何减少重复造轮子,这些都是我们要掌握的问题。本期,我们就利用 spring 中如何解决,依赖注入(Dependency Injection),循环依赖怎么解决;
循环依赖
循环依赖,通常是指 Bean 对象之间的互相依赖,比如 A 对象创建需要 B 对象,也就是 A 依赖于 B,依赖类推,B 依赖于 C,C 依赖于 A, 形成一个完整的闭环,以下图为:
说说 spring 容器中的循环依赖:
每个对象是单例的对象,互相依赖的时候,可以直接调用,
spring 底层怎么保证 AB 循环依赖的问题?
所以说,对于循环多次依赖,我们要求的是,单例且 set 注入------>可以避免 BeanCurrentlyInCreationException 的问题
spring 循环依赖的注入方式
实例化构造器注入带来的 beancurrentlyIncreationException 的问题;模拟 A 和 B 两个实例对象,在循环依赖中,利用构造器互相注入,看能否实现循环依赖:
创建三个类:
package com.atguowang.thirdinterview.spring.aroudyilai.constructor;
import org.springframework.stereotype.Component;
/**
@author shkstart
@create 2020-11-12 16:51*/@Componentpublic class ServiceA {
private ServiceB serviceB;
public ServiceA(ServiceB serviceB) {//当前私有的 serviceB--this.serviceB=serviceB;}
}
package com.atguowang.thirdinterview.spring.aroudyilai.constructor;
import org.springframework.stereotype.Component;
/**
@author shkstart
@create 2020-11-12 16:51*/@Componentpublic class ServiceB {
private ServiceA serviceA;
public ServiceB(ServiceA serviceA ) {this.serviceA=serviceA;}}
package com.atguowang.thirdinterview.spring.aroudyilai.constructor;
import sun.applet.Main;
/**
@author shkstart
@create 2020-11-12 16:51*/public class TestConstructor {public static void main(String[] args) {
}
}
结论:
因为我们要创建一个 A,就要在构造器加载过程中,加入一个 B,但是又 要重复创建 A,和 B;constructor 无法解决这个问题;
2:利用 set 注入进行 spring 的循环依赖注入 package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.SetInjection;
import org.springframework.stereotype.Component;
/**
@author lucas
@create 2020-12-12 17:13*/@Componentpublic class ServiceBB {
private ServiceAA serviceAA;
public void setServiceAA(ServiceAA serviceAA){this.serviceAA=serviceAA;System.out.println("B 中得到这个方法 A 的赋值");}}
package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.SetInjection;
import org.springframework.stereotype.Component;
/**
@author lucas
@create 2020-12-12 17:13*/@Componentpublic class ServiceBB {
private ServiceAA serviceAA;
public void setServiceAA(ServiceAA serviceAA){this.serviceAA=serviceAA;System.out.println("B 中得到这个方法 A 的赋值");}}
循环依赖 set 方法注入,可以依赖实现;
package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.SetInjection;
import sun.java2d.pipe.AAShapePipe;
/**
@author lucas
@create 2020-12-12 17:13*/public class TestSetInjection {
public static void main(String[] args) {/*** 测试 set 方法注入的循环依赖*/ServiceAA aa = new ServiceAA();ServiceBB bb = new ServiceBB();
}
}
spring 容器化
减少通讯的资源损耗
节省对象复用
更好的控制对象
创建符合要求的初始化对象
我们现在开始觊觎 spring 容器化来实现循环依赖到底怎么玩?
对象 A:
package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.springIOC;
/**
@author maweibo
@create 2021-01-05 10:09
@description spring 容器中关于循环依赖的实现*/public class A {
private B b;
public B getB() {return b;}
public void setB(B b) {this.b = b;}
A(){System.out.println(" A -created- success");}
}
对象 B:
package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.springIOC;
/**
@author maweibo
@create 2021-01-05 10:10
@description spring 容器中*/
public class B {
}
package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.springIOC;
/**
@author maweibo
@create 2021-01-05 10:10
@description set 方法实现*/
public class B {
}
建议一下,set 方式注入是没问题的,然后我们在基础上添加 spring 容器的配置文件,注意看好添加的位置;
application.xml:
开始做 spring 容器实例化创建两个对象,进行循环依赖;
package com.atguowang.thirdinterview.spring.aroudyilai.depencyInjection.springIOC;
import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
@author maweibo
@create 2021-01-05 10:38
@description spring 单例容器化实现循环依赖*/public class ClientSpringcontainer {public static void main(String[] args) {
}}
注意事项:
第一次开始的时候,可能会遇见 bean 不存在,注意建立时候配置文件的位置,检查 A 和 B 的位置,我准备了链接,给你谈谈路,
如何优雅的解决:
结果如图所示:
报出的错误,和我们之前构造器注入方式一致,都是 BeanCurrentlyInCreationException:bean 对象一致处于创建内层的循环中,发生异常,也就说当 scope 属性不是单例的时候,无法解决 spring 循环依赖的问题;
三级缓存
IDEA 的同学呢,可以那两次 shift 键,查询这个类的具体实现过程;
package org.springframework.beans.factory.support;public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {protected static final Object NULL_OBJECT = new Object();protected final Log logger = LogFactory.getLog(this.getClass());private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); //一级 单例-成品 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); //三级--工厂 beanprivate final Map<String, Object> earlySingletonObjects = new HashMap(16); //二级 半成品
三级缓存只是适用于,对象是单例 bean 对象,每次创建 bean 对象;
注意事项:
scope=phototype
解答:
对于每次创建一个对象 bean,不是单例对象,所以不会进入三级缓存,也就不能通过三级缓存--->解决循环依赖问题
理解:
因为我们第一次创建的对象是基于单例的,所有单例的对象走完了完整的生命周期--进入缓存--然后才开始可以解决循环依赖
由于 photoType 每次要重新创建一个对象,所以无法放入缓存中,也就不能使用三级缓存来解决循环依赖
写到这里,关于 spring 循环的问题就告一段落,其实还有下篇,集中对于如何对象 bean 实现三级缓存的细节,我们来处理循环依赖,下节我们接着干,希望你会有所收获,我们一起进步,相互成就才是最好的状态。
大家晚安,好梦,我是卢卡,感兴趣就点个赞,再走吧
版权声明: 本文为 InfoQ 作者【卢卡多多】的原创文章。
原文链接:【http://xie.infoq.cn/article/7662a89774aec05407d327a51】。文章转载请联系作者。
评论