循环依赖的底层原理

从 Spring IOC 容器中获取 bean 实例的流程:从context.getBean()方法开始


发生了循环依赖:

public class A {
    @Autowired
    private B b;
}
public class B {
  @Autowired
    private A a;
}

获取单例

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从一级缓存(单例池)中查找对象
        Object singletonObject = this.singletonObjects.get(beanName);
//单例池中找不到,而且被查找的bean正在创建中--发生了循环依赖
//通过singletonsCurrentlyInCreation(set)记录对象是否正在被创建
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
//从二级缓存中去寻找 原始对象或者是代理对象
                singletonObject = this.earlySingletonObjects.get(beanName);
//二级缓存中找不到 就去三级缓存中找
                if (singletonObject == null && allowEarlyReference) {
//这里取得三级缓存中对应的lambda表达式的值--得到原始对象(发生循环依赖但未aop)或者是代理对象(发生循环依赖和aop--提前aop)
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
//执行上一步获取的lambda表达式(getEarlyBeanReference方法)
//--有aop,就提前执行AOP--得到一个代理对象
//-- 无aop,就得到一个原始对象
                        singletonObject = singletonFactory.getObject();// 提前曝光 bean 实例(raw bean),用于解决循环依赖
//将通过三级缓存得到的结果 放入二级缓存
                        this.earlySingletonObjects.put(beanName, singletonObject);
//移除三级缓存中对应的内容
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

三级缓存为什么会移除掉?
在三级缓存中找 找到了就会执行AOp 产出代理对象 然后将代理对象放入到二级缓存。三级缓存一定会能到的对象 但不一定 会执行 aop 二级缓存找不到 才会触发aop(通过lambda表达式,执行函数式接口,执行aop) 产生代理对象放入二级缓存,放入之后需要移除掉对应的三级缓存(保证只执行一次aop)如果三级缓存中对象不需要执行aop操作 ,那么产生的对象仍然要放入二级缓存 ,这是放入的对象是原始对象

为什么单例池:concurentHashmap是,二级缓存是hashmap,三级缓存是hashmap?
三级缓存的AOP过程需要加锁以保证操作的原子性
因为三级缓存的函数接口 内部已经加了锁,保证了操作的原子性 所以没必要使用concurenthashmap

问题:源码中加synchronized锁的意义?

背景:
二级缓存中的aService对象是三级缓存中的lambda表达式生成出来的,
他们是成对的,二级缓存中存在代理对象,则三级缓存中不应该存在lambda表达式;
或者说,三级缓存中存在lambda表达式,则二级缓存中不应当有该代理对象

解答:

  • 3级缓存中的 value是一个lambda表达式,一执行就是进行AOP,得到代理对象,所以lambda表达式应当只执行一次,且执行完毕后从3级缓存中进行移除,以防止其他的代码又拿出来执行了
  • 在第2、第3级缓存只能有一个地方存在操作对象,要么是lambda表达式(三级缓存),要么是lambda执行后的代理对象(二级缓存)。这是原子性的,为了对高并发情况进行控制,加锁进行同步。
  • 1级缓存定义为 concurrentHashMap。 2级、3级缓存定义为简单的HashMap,是因为 2、3级缓存是成对出现的,哪怕是定义成concurrentHashMap,也要加锁保持两个Map的操作的原子性

创建Bean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
//得到原始对象 -- 属性未赋值
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        // Allow post-processors to modify the merged bean definition.
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
//如果当前创建的是单例bean,并且允许着环依赖,并且还在创建过程中,那么则提早暴露--一般均为true 
//创建就暴露--一般单例都会暴露出去--都会存入三级缓存
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
//针对发生了循环依赖的情况
//lambda执行的结果存入三级缓存中
//getEarlyBeanReference--wrapIfNecessary方法--判断对象创建过程中是否存在AOP
//--需要,就提前aop,存入代理对象,不需要就存入原始对象
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // Initialize the bean instance.
//暴露原始对象
        Object exposedObject = bean;
        try {
//属性填充@Autowired --B对象填充原始对象 
            populateBean(beanName, mbd, instanceWrapper);
//正常情况下进行AOP的地方--postProcessAfterInitialization方法
//  --判断是否提前进行了AOP(使用Map:earlyProxyReferences记录提前 进行的aop),如果没有提前aop,才会在这里执行aop
//初始化Bean--可能包含AOP--如果需要AOP,则需要使用原始对象(针对非循环依赖的时候)
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

    ····················
        // Register bean as disposable.
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }

        return exposedObject;
    }

其中:

 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

是将原始对象信息存入三级缓存的操作,存入的是lambda表达式:
getEarlyBeanReference(beanName, mbd, bean)执行的结果,getEarlyBeanReference会对是否需要提前AOP进行判断,如果需要进行AOP,则生成代理对象放入二级缓存。

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);
            }
        }
    }

其中:

exposedObject = initializeBean(beanName, exposedObject, mbd);
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
//对类中某些特殊方法的调用,比如 @PostConstruct,Aware接口
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
//InitializingBean接口,afterPropertiesSet,init-method属性调用
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
//AOP入口
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

整个流程:

1、A doCreateBean()初始化,由于还未创建,从一级缓存查不到,此时只是一个半成品(提前暴露的对象),放入三级缓存singletonFactories;
2、A发现自己需要B对象,但是三级缓存中未发现B,创建B的原始对象,将带有B对象信息(beanName,bd,原始对象)的Lambda表达式放入singletonFactories;
3、B发现自己需要A对象,从一级缓存singletonObjects没找到,并知道了A对象正在创建,就确认发生了循环依赖,这时候再去二级缓存earlySingletonObjects中寻找A对象,没找到就继续在三级缓存singletonFactories中寻找A对象(一定能找到),于是执行三级缓存中的lambda表达式得到一个代理对象或者是原始对象A(A中属性未赋值),将A放入二级缓存earlySingletonObjects,同时从三级缓存删除对应beanName的表达式;
同理向三级缓存加入对象时,也会从二级缓存中将相同BeanName的记录删除掉,所以二级缓存与三级缓存的之间的来两步操作具有原子性。

this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);

4、将A注入到对象B中;
5、B完成属性填充,执行初始化方法,将自己放入第一级缓存中(此时B是一个完整的对象);
6、A得到对象B,将B注入到A中;
7、A完成属性填充,初始化,并放入到一级缓存中

注意:在对象创建开始的时候,会对对象创建状态利用Set:
singletonsCurrentlyInCreation进行记录:是否是正在创建,可用于判断是否发生了循环依赖。

@Lazy注解的作用:

initializeBean
Spring解决循环依赖问题--B站视频讲解
Spring 循环依赖的“常见”面试问题

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容