大奖18dj18vip-大奖18dj18娱乐官网

海外1核2G服务器低至2折,半价续费券限量免费领取!

大奖18dj18vip

查看: 26|回复: 0
打印 上一主题 下一主题

[资讯] Spring解决循环依赖的3种方式!

[复制链接]
  • TA的每日心情
    奋斗
    2020-2-20 10:37
  • 签到天数: 605 天

    [LV.9]以坛为家II

    硕士生

    1万

    主题

    1万

    帖子

    3万

    积分

    Rank: 8Rank: 8

    UID
    15343
    威望
    -561
    贡献
    8050
    在线时间
    299 小时
    注册时间
    2015-10-12
    跳转到指定楼层
    楼主
    发表于 2020-2-11 10:39 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

    循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错。

    下面说一下Spring是如果解决循环依赖的。

    第一种:构造器参数循环依赖

    Spring容器会将每一个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中。

    因此如果在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖;而对于创建完毕的Bean将从“当前创建Bean池”中清除掉。

    首先我们先初始化三个Bean。

    • public class StudentA {
    •     private StudentB studentB ;
    •     public void setStudentB(StudentB studentB) {
    •         this.studentB = studentB;
    •     }
    •     public StudentA() {
    •     }
    •     public StudentA(StudentB studentB) {
    •         this.studentB = studentB;
    •     }
    • }
    • public class StudentB {
    •     private StudentC studentC ;
    •     public void setStudentC(StudentC studentC) {
    •         this.studentC = studentC;
    •     }
    •     public StudentB() {
    •     }
    •     public StudentB(StudentC studentC) {
    •         this.studentC = studentC;
    •     }
    • }
    • public class StudentC {
    •     private StudentA studentA ;
    •     public void setStudentA(StudentA studentA) {
    •         this.studentA = studentA;
    •     }
    •     public StudentC() {
    •     }
    •     public StudentC(StudentA studentA) {
    •         this.studentA = studentA;
    •     }
    • }

    OK,上面是很基本的3个类,StudentA有参构造是StudentB。StudentB的有参构造是StudentC,StudentC的有参构造是StudentA ,这样就产生了一个循环依赖的情况。

    我们都把这三个Bean交给 Spring 管理,并用有参构造实例化。

    •    
    •    
    •    

    下面是测试类:

    • public class Test {
    •     public static void main(String[] args) {
    •         ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
    •         //System.out.println(context.getBean("a", StudentA.class));
    •     }
    • }

    执行结果报错信息为:

    • Caused by: org.springframework.bean**actory.BeanCurrentlyInCreationException:
    •   Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

    如果大家理解开头那句话的话,这个报错应该不惊讶,Spring容器先创建单例StudentA,StudentA依赖StudentB,然后将A放在“当前创建Bean池”中。

    此时创建StudentB,StudentB依赖StudentC ,然后将B放在“当前创建Bean池”中,此时创建StudentC,StudentC又依赖StudentA。

    但是,此时Student已经在池中,所以会报错,因为在池中的Bean都是未初始化完的,所以会依赖错误 ,初始化完的Bean会从池中移除。

    第二种:setter方式单例,默认方式

    如果要说setter方式注入的话,我们最好先看一张Spring中Bean实例化的图

    如图中前两步骤得知:Spring是先将Bean对象实例化之后再设置对象属性的,Spring 中的 bean 为什么默认单例, 这篇建议大家看下。

    关注微信公众号:Java技术栈,在后台回复:spring,可以获取我整理的 N 篇最新 Spring 教程,都是干货。

    修改配置文件为set方式注入

    •    
    •    
    •    

    下面是测试类:

    • public class Test {
    •     public static void main(String[] args) {
    •         ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
    •         System.out.println(context.getBean("a", StudentA.class));
    •     }
    • }

    打印结果为:

    • com.zfx.student.StudentA@1fbfd6

    为什么用set方式就不报错了呢 ?

    我们结合上面那张图看,Spring先是用构造实例化Bean对象 ,此时 Spring 会将这个实例化结束的对象放到一个Map中,并且 Spring 提供了获取这个未设置属性的实例化对象引用的方法。

    结合我们的实例来看,当Spring实例化了StudentA、StudentB、StudentC后,紧接着会去设置对象的属性,此时StudentA依赖StudentB,就会去Map中取出存在里面的单例StudentB对象,以此类推,不会出来循环的问题喽、

    下面是Spring源码中的实现方法。以下的源码在Spring的Bean包中的DefaultSingletonBeanRegistry.java类中

    • /** Cache of singleton objects: bean name --> bean instance(缓存单例实例化对象的Map集合) */
    • private final Map singletonObjects = new ConcurrentHashMap(64);
    • /** Cache of singleton factories: bean name --> ObjectFactory(单例的工厂Bean缓存集合) */
    • private final Map singletonFactories = new HashMap(16);
    • /** Cache of early singleton objects: bean name --> bean instance(早期的单身对象缓存集合) */
    • private final Map earlySingletonObjects = new HashMap(16);
    • /** Set of registered singletons, containing the bean names in registration order(单例的实例化对象名称集合) */
    • private final Set registeredSingletons = new LinkedHashSet(64);
    • /**
    • * 添加单例实例
    • * 解决循环引用的问题
    • * Add the given singleton factory for building the specified singleton
    • * if necessary.
    • * To be called for eager registration of singletons, e.g. to be able to
    • * resolve circular references.
    • * @param beanName the name of the bean
    • * @param singletonFactory the factory for the singleton object
    • */
    • protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
    •   Assert.notNull(singletonFactory, "Singleton factory must not be null");
    •   synchronized (this.singletonObjects) {
    •     if (!this.singletonObjects.containsKey(beanName)) {
    •       this.singletonFactories.put(beanName, singletonFactory);
    •       this.earlySingletonObjects.remove(beanName);
    •       this.registeredSingletons.add(beanName);
    •     }
    •   }
    • }

    第三种:setter方式原型,prototype

    修改配置文件为:

    •    
    •    
    •    

    scope="prototype" 意思是 每次请求都会创建一个实例对象。

    两者的区别是:有状态的bean都使用Prototype作用域,无状态的一般都使用singleton单例作用域。

    测试用例:

    • public class Test {
    •     public static void main(String[] args) {
    •         ApplicationContext context = new ClassPathXmlApplicationContext("com/zfx/student/applicationContext.xml");
    •         //此时必须要获取Spring管理的实例,因为现在scope="prototype" 只有请求获取的时候才会实例化对象
    •         System.out.println(context.getBean("a", StudentA.class));
    •     }
    • }

    打印结果:

    • Caused by: org.springframework.bean**actory.BeanCurrentlyInCreationException:
    •     Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

    为什么原型模式就报错了呢 ?

    对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。


    楼主热帖排行榜
    大奖18dj18vip社区温馨提示:
    大奖18dj18vip(www.dastanona.com)十分重视网络版权及其他知识产权的保护,针对网络侵权采取如下版权政策:
    1、大奖18dj18vip有理由相信网友侵犯任何人的版权或作品,(图文,文字,下载,视频,非法传播),大奖18dj18vip有权不事先通知即删除涉嫌侵权的作品和内容
    2、大奖18dj18vip将采取必要的网络技术手段,确认为侵权作品或内容的用户有权进行警告、屏蔽、删除的行为,尽可能的防止侵权行为的发生
    3、大奖18dj18vip影视资源均收集自互联网,没有提供影片资源存储,也未参与录制上传,若大奖18dj18vip收录的资源涉及您的版权或知识产权或其他利益,我们会立即删除
    4、大奖18dj18vip,删帖,投诉,举报,侵权,若大奖18dj18vip侵犯您的权益,附上身份及权利证明,请直接发送邮件到 kefu-sosoba@qq.com 我们将在一个工作日内删除
    soso大奖18dj18vip社区是聚合百度搜索,搜狗搜索,360搜索,新闻,教育,站长,广告,娱乐,影视,微信,网盘,营销,手机,汽车,游戏,论坛综合为一体的大型门户社区www.dastanona.com
    【腾讯云】腾讯云服务器安全可靠高性能,多种配置供您选择
    Powered by www.dastanona.com Copyright © 2013-2020 大奖18dj18vip社区 小黑屋|手机版|地图|关于我们|腾讯云代金券|帮助中心|soso吧社区
    广告服务/项目合作: kefu-sosoba@qq.com  侵权举报邮箱: kefu-sosoba@qq.com  大奖18dj18vip建站时间:创建于2013年07月23日
    免责声明:大奖18dj18vip所有的内容均来自互联网以及第三方作者自由发布,版权归原作者版权所有,大奖18dj18vip不承担任何的法律责任,若有侵权请来信告知,我们立即删除!

    GMT+8, 2020-2-21 08:49 , Processed in 1.134683 second(s), 11 queries , Gzip On, MemCache On.

    快速回复 返回顶部 返回列表