上一篇博客简单地分析了下依赖注入。但是对于依赖注入的很多细节,都没有深入的分析。这一篇博客会继续分析spring的依赖注入。这篇博客会解决分析getBean缓存时候遗留下来的循环依赖问题。

循环依赖分析

首先明确下,只有单例情况下,spring才会试着去解决循环依赖问题,多例是不会去解决循环依赖的。这个也好理解,如果是多例的话,比如a -> b 并且 b -> a 那么,当A a=new A(); 之后要注入b,b却是多例的,那么究竟该注入哪个B是不确定的。如下图:

接下来我们分析,为啥会有循环依赖的问题。

先来分析没有循环依赖的问题public static class A{

private B b;    //省略get和set方法}public static class B{

}

这个时候,如果spring先初始化A,然后会发现A依赖于B,然后就会初始化B,最后注入到A里。

整个流程用代码表示大概如下所示:A a = 创建A

B b = 创建B

-----> 创建A子流程  a.setB(b);

但是假设B也依赖了A呢?即public static class B{

private A a;

}

那样依赖注入过程就会变成(简单示例)A a = 创建A

B b = 创建B

---->  创建B子流程 b.setA(????);  //  这个时候A还没创建完成呢a.setB(b);

那么如何解决呢?很简单,把那个还没创建完的A(只是new了,但是没有进行依赖注入的A)set到B里就好了。

弄清了这个流程之后,我们再来分析spring是如何进行依赖注入的。

spring对引用类型的注入

这里我先从spring对引用属性的注入开始。

即ref的注入private Object resolveReference(Object argName, RuntimeBeanReference ref) {    try {        String refName = ref.getBeanName();

refName = String.valueOf(doEvaluate(refName));        if (ref.isToParent()) {            if (this.beanFactory.getParentBeanFactory() == null) {                throw new BeanCreationException(                        this.beanDefinition.getResourceDescription(), this.beanName,                        "Can't resolve reference to bean '" + refName +                        "' in parent factory: no parent factory available");

}            return this.beanFactory.getParentBeanFactory().getBean(refName);

}        else {            Object bean = this.beanFactory.getBean(refName);            this.beanFactory.registerDependentBean(refName, this.beanName);            return bean;

}

}    catch (BeansException ex) {        throw new BeanCreationException(                this.beanDefinition.getResourceDescription(), this.beanName,                "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);

}

}

实现很简单,就是从beanFactory里获取要依赖的对象

我们再来回顾下流程

问题是,当走到6时候,似乎又会回到1,这样不就死循环了么?

重点就是,第六步的获取A,我们回到doGetBean方法中。

getSingleton 方法protected Object getSingleton(String beanName, boolean allowEarlyReference) {    //已创建的对象里面找下

Object singletonObject = this.singletonObjects.get(beanName);    //没找到,并且当前类正在被创建

if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {        synchronized (this.singletonObjects) {

singletonObject = this.earlySingletonObjects.get(beanName);            if (singletonObject == null && allowEarlyReference) {

ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);                if (singletonFactory != null) {

singletonObject = singletonFactory.getObject();                    this.earlySingletonObjects.put(beanName, singletonObject);                    this.singletonFactories.remove(beanName);

}

}

}

}    return (singletonObject != NULL_OBJECT ? singletonObject : null);

}

我们在分析初始化bean的缓存部分时,曾分析过这几个缓存。当时其实只知道了singletonObjects是存储了已经创建了的对象。

现在让我们回头再看看这些缓存。

首先看看singletonFactories赋值的地方。

doCreateBean// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&

isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {    if (logger.isDebugEnabled()) {

logger.debug("Eagerly caching bean '" + beanName +                "' to allow for resolving potential circular references");

}

addSingletonFactory(beanName, new ObjectFactory() {        @Override

public Object getObject() throws BeansException {            //默认实现返回bean

return getEarlyBeanReference(beanName, mbd, bean);

}

});

}

在创建bean时候,有这样一段代码,当需要进行提前暴露时候(当前创建对象单例 + 允许循环引用 + 当前类正在被创建)会调用addSingletonFactory方法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);

}

}

}

这里就碰见了我们要分析的singletonFactories,它存储了beanName -> 此处的匿名内部类singletonFactory。

singletonFactory

我们再回到getSingleton方法,以及之前绘制的图ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {

singletonObject = singletonFactory.getObject();    this.earlySingletonObjects.put(beanName, singletonObject);    this.singletonFactories.remove(beanName);

}

逻辑很简单

三级缓存

我们经常听说spring通过三级缓存解决了循环依赖,其实三级缓存非常简单。就是指我们分析过的Map singletonObjects

Map> singletonFactories

Map earlySingletonObjects

这里分别举这样三个例子。

A 依赖 B(B不依赖A)

一级缓存

A 依赖 B && B依赖A

三级缓存

A依赖B && B依赖A + B依赖C && C 依赖 A

作者:端吉

链接:https://www.jianshu.com/p/10f94b776e55

java 注入 循环_spring依赖注入——循环依赖相关推荐

  1. spring 循环依赖注入

    什么是循环依赖 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比如A引用B,B引用C,C引用A,则它们最终反映为一个环. spring 中循环依赖注入分三种情况 1. 构造器循环依赖 ...

  2. Spring 依赖注入三种方式的实现,及循环依赖问题的解决(源码+XML配置)

    搬砖啦,搬砖啦,这几天在看Spring相关的书,下面给大家分享一下这几天的心得与收获,Go Go Go! Spring支持两种依赖注入方式,分别是属性注入,构造函数注入.除此之外,Spring还支持工 ...

  3. java循环依赖问题怎么解决_Spring如何解决循环依赖的问题

    前言 在面试的时候这两年有一个非常高频的关于spring的问题,那就是spring是如何解决循环依赖的.这个问题听着就是轻描淡写的一句话,其实考察的内容还是非常多的,主要还是考察的应聘者有没有研究过s ...

  4. Spring依赖注入和循环依赖问题分析

    Spring源码揭秘之依赖注入和循环依赖问题分析 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGet ...

  5. 6. 以下耦合度中最松散的耦合是_Spring Java中的依赖注入,它是如何工作的?- 知识铺...

    知识铺: 致力于打造轻知识点,持续更新每次的知识点较少,阅读不累.不占太多时间,不停地来唤醒记忆深处的知识点. 一.Spring 框架 POJO Spring Framework是一个开源的应用程序框 ...

  6. Android mock for循环,Android单元测试(五):依赖注入,将mock方便的用起来

    在上一篇文章中,咱们讲了要将mock出来的dependency真正使用起来,须要在测试环境下经过某种方式set 到用到它的那个对象里面进去,替换掉真实的实现.咱们前面举的例子是:html public ...

  7. java什么是依赖注入_spring的依赖注入是什么意思

    最近学习spring框架,对依赖注入有些模糊,遂上网翻阅资料,做了下列总结,原博客为CSDN 南夏的 spring的依赖注入是什么意思,侵删! Spring 能有效地组织J2EE应用各层的对象.不管是 ...

  8. java 构造器注入_Spring学习笔记1—依赖注入(构造器注入、set注入和注解注入)...

    什么是依赖注入 在以前的java开发中,某个类中需要依赖其它类的方法时,通常是new一个依赖类再调用类实例的方法,这种方法耦合度太高并且不容易测试,spring提出了依赖注入的思想,即依赖类不由程序员 ...

  9. Java 依赖注入标准(JSR-330)简介

    Java 依赖注入标准(JSR-330)简介 转载请保留作者信息: 作者:88250 ,Vanessa 时间:2009 年 11 月 19 日 Java 依赖注入标准(JSR-330,Dependen ...

最新文章

  1. 取代Python多进程!伯克利开源分布式框架Ray
  2. React从入门到精通系列之(1)安装React
  3. Linux数据报文接收发送总结3
  4. [导入]C#面向对象设计模式纵横谈(17):(行为型模式) Mediator 中介者模式.zip(8.75 MB)...
  5. How to get user parameter settings
  6. BZOJ2818-莫比乌斯反演/欧拉函数
  7. LintCode 795. 4种独特的路径(DFS)
  8. 【第二组】项目冲刺(Alpha版本)第三次每日例会 2017/7/13
  9. xclip linux_使用xclip在Linux命令行中复制和粘贴
  10. HDU1754 I Hate It (线段树单点修改+区间查询)
  11. java面向对象简介
  12. IDEA上的文字转拼音名
  13. python中ctype的应用,协议解析,C语言与python的完美映射,结构体与字符串的相互转换
  14. (七)HyperledgerFarbic1.4- Fabric的SDK使用
  15. Python做一份简易旅行攻略——疫情之后,若条件允许,可愿意用一场旅行“弥补”自己
  16. 明年债券收益率有望延续下行的趋势
  17. 电缆公司如何面对企业改革?MES系统打造智能工厂
  18. 08 量子力学教材推荐,量子力学书单:量子力学、高等量子力学、量子统计、量子信息、路径积分...(适合物理专业本科生、研究生、物理爱好者)
  19. PSD文件生成Unity预设
  20. 西方科学家依然对互联网的进化表示质疑

热门文章

  1. 前端学习(1):HTML和CSS导学
  2. CM3计算板EC20模组拨号上网
  3. eclipse启动报错No java virtual machine was found after seearching the locations:XXXXX
  4. :after伪类+content内容清除浮动
  5. 二维数组排序 行与列分别升序_6个经典排序技巧,尤其是最后一个,绝对的个性化...
  6. 百度地图gif图标_华为手机误删照片怎么找回?手机怎么快速制作GIF动图
  7. 第八届蓝桥杯-日期问题
  8. redis 超时失效key 的监听触发
  9. 【卡法电子商务】-常用手机屏幕尺寸 ★★★★★
  10. HDU 1394 线段树or 树状数组~