日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Spring循環(huán)依賴那些事兒(含Spring詳細(xì)流程圖)

背景

1、循環(huán)依賴異常信息

  • 應(yīng)用時(shí)間時(shí)間久
  • 應(yīng)用多人同時(shí)并行開(kāi)發(fā)
  • 應(yīng)用保證迭代進(jìn)度

經(jīng)常出現(xiàn)啟動(dòng)時(shí)出現(xiàn)循環(huán)依賴異常

創(chuàng)新互聯(lián)于2013年開(kāi)始,先為義馬等服務(wù)建站,義馬等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為義馬企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskPunchEvent': Injection of resource dependencies failed; nested exception is org.
springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'playContentService': Bean with name 'playContentService' has been injected into other be
ans [toVoConvertor] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. Thi
s is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
  at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:325)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1404)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
  at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
  at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1255)
  at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175)
  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:595)
  ... 40 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'playContentService': Bean with name 'playContentService' has been injecte
d into other beans [toVoConvertor] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version o
f the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:622)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
  at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.resolveBeanByName(AbstractAutowireCapableBeanFactory.java:452)
  at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:527)
  at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:497)
  at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:637)
  at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:180)
  at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
  at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:322)
  ... 51 more

2、依賴關(guān)系

先不關(guān)注其他不規(guī)范問(wèn)題,看現(xiàn)象

3、涉及基礎(chǔ)知識(shí)

  • Spring bean 創(chuàng)建流程
  • Dynamic Proxy 動(dòng)態(tài)代理
  • Spring-AOP 原理

問(wèn)題

1、什么是循環(huán)依賴?

2、為什么會(huì)產(chǎn)生循環(huán)依賴?

3、循環(huán)依賴有哪些場(chǎng)景?

4、Spring如何解決循環(huán)依賴的?

5、Spring為什么使用三級(jí)緩存?

6、Spring支持AOP循環(huán)依賴,為何還存在循環(huán)依賴異常?

7、Spring不支持的循環(huán)依賴場(chǎng)景及如何解決?

注:Spring啟動(dòng)流程與Bean創(chuàng)建初始化流程如不熟悉,自行補(bǔ)習(xí),篇幅原因此處不做介紹

Spring循環(huán)依賴

1、什么是循環(huán)依賴

2、核心概念

  • BeanDefinition:spring核心bean的配置信息
  • Spring Bean:spring管理的已經(jīng)初始化好以后的可使用的實(shí)例

首先,通過(guò)spring通過(guò)掃描各種注解 @Compoent、@Service、@Configuration等等把需要交給spring管理的bean初始化成 BeanDefinition 的列表

然后,根據(jù) BeanDefinition 創(chuàng)建spring bean的實(shí)例

  • Java Bean:Java簡(jiǎn)單通過(guò)構(gòu)造函數(shù)創(chuàng)建的對(duì)象
  • Spring通過(guò)推斷構(gòu)造方法后,通過(guò)反射調(diào)用構(gòu)造函數(shù)創(chuàng)建的對(duì)象

3、什么情況下出現(xiàn)循環(huán)依賴

并非使用者手動(dòng)去getBean才會(huì)加載并初始化,而是框架啟動(dòng)時(shí)進(jìn)行加載

Spring創(chuàng)建Bean - #DefaultListableBeanFactory#preInstantiateSingletons


@Override
public void preInstantiateSingletons() throws BeansException {
    
    //......
    
    List beanNames = new ArrayList<>(this.beanDefinitionNames);


    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                //FactoryBean接口處理
                ......
            }
            else {
                //正常Bean的加載入口
                getBean(beanName);
            }
        }
    }
    
    //......
}

4、循環(huán)依賴場(chǎng)景

  • 構(gòu)造器內(nèi)的循環(huán)依賴

注入的好處很明顯,如果容器中不存在或者存在多個(gè)實(shí)現(xiàn)時(shí),可以從容處理。

強(qiáng)依賴,先有雞還是先有蛋問(wèn)題暫無(wú)解,此依賴方式Spring不支持,除非自身實(shí)現(xiàn)代理加延遲注入,這種方式很難解決,除非實(shí)現(xiàn)類(lèi)似于lazy生成代理方式進(jìn)行解耦來(lái)實(shí)現(xiàn)注入,Spring沒(méi)有支持可能因?yàn)榇朔N注入場(chǎng)景都可以用其他方式代替且場(chǎng)景極少。

弱依賴,spring 4.3之后增加 ObjectProvider 來(lái)處理

//構(gòu)造器循環(huán)依賴示例


public class StudentA {
 
    private StudentB studentB ; 


    public StudentA(StudentB studentB) {
        this.studentB = studentB;
    }
}


public class StudentB {
 
    private StudentA studentA ;
    
    public StudentB(StudentA studentA) {
        this.studentA = studentA;
    }
}
  • setter方式單例,默認(rèn)方式
  • setter方式原型,prototype
    對(duì)于“prototype”作用域Bean,Spring容器不進(jìn)行緩存,因此無(wú)法提前暴露一個(gè)創(chuàng)建中的Bean。
  • field屬性循環(huán)依賴
    最常用,此場(chǎng)景是通過(guò)反射注入,以下為@Autowire 注入代碼,@Resource省略AutowiredAnnotationBeanPostProcessor#postProcessProperties
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        //屬性注入
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

5、三級(jí)緩存解決循環(huán)依賴

(1)一級(jí)緩存

DefaultSingletonBeanRegistry

private final Map singletonObjects = new ConcurrentHashMap<>(256);
  • 最基礎(chǔ)的單例緩存
  • 限制 bean 在 beanFactory 中只存一份,即實(shí)現(xiàn) singleton scope

(2)二級(jí)緩存

二級(jí)緩存(未初始化未填充屬性提前暴露的Bean)

private final Map earlySingletonObjects = new HashMap<>(16);
  • 看名字應(yīng)該就能猜到,緩存earlySingletonBean,與三級(jí)緩存配合使用的
  • 需要注意:
  • 在沒(méi)有AOP場(chǎng)景時(shí)是可以的,每次earlySingletonObjects.get()換成去三級(jí)緩存取就可以,存在問(wèn)題
  • 存在AOP場(chǎng)景時(shí)
  • 因此,讓使用者去做重復(fù)性判斷是不可控的,很容易出現(xiàn)問(wèn)題,于是引入了第二級(jí)緩存,當(dāng)調(diào)用三級(jí)緩存里的對(duì)象工廠的getObject方法之后,getEarlyBeanReference 就會(huì)把返回值放入二級(jí)緩存,刪除三級(jí)緩存,后續(xù)其他依賴該對(duì)象的Bean獲取的都是同一個(gè)earlyBean,保證singleton原則。
  • 每次都調(diào)用 getEarlyBeanReference,即使返回對(duì)象都一致,也浪費(fèi)不必要時(shí)間
  • 如果使用者在 getEarlyBeanReference 時(shí)直接 new XXX(),則對(duì)象又不一致,無(wú)法保證 singleton,所以需要使用者熟悉這塊原理,并且自身維護(hù),并且暴露內(nèi)部實(shí)現(xiàn)細(xì)節(jié)
  • 每次都調(diào)用 getEarlyBeanReference 返回代理對(duì)象都不一致,無(wú)法保證 singleton
  • 如果沒(méi)有此緩存,可不可以解決循環(huán)依賴問(wèn)題?

(3)三級(jí)緩存

三級(jí)緩存(Bean創(chuàng)建時(shí)提供代理機(jī)會(huì)的Bean工廠緩存)

private final Map> singletonFactories = new HashMap<>(16);
  • 所以二級(jí)緩存和三級(jí)緩存是組合,不要拆成兩個(gè)獨(dú)立的東西去理解
  • 基于這種設(shè)計(jì),沒(méi)有發(fā)生循環(huán)依賴的bean就是正常的創(chuàng)建流程
  • 相互引用的bean 會(huì)觸發(fā)鏈路中最初結(jié)點(diǎn)放入三級(jí)緩存內(nèi)容,調(diào)用 getEarlyBeanReference 返回相應(yīng)對(duì)象

6、Spring為何不使用一級(jí)、二級(jí)緩存解決循環(huán)依賴

循環(huán)依賴產(chǎn)生在Bean創(chuàng)建時(shí)

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    
    BeanWrapper instanceWrapper = null;


    if (instanceWrapper == null) {
        //創(chuàng)建Bean
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
        
    .....
    
    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");
        }
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }


    //填充Bean依賴與Bean的初始化
    Object exposedObject = bean;
    try {
        //填充依賴的bean實(shí)例
        populateBean(beanName, mbd, instanceWrapper);
        //初始化---注意!注意!注意!此方法中可能調(diào)用 BeanPostProcessor
        //的applyBeanPostProcessorsAfterInitialization時(shí)可能會(huì)返回代理對(duì)象,如果代理途徑與創(chuàng)建時(shí)代理方式不同則也會(huì)產(chǎn)生不同代理對(duì)象
        //從而產(chǎn)生循環(huán)依賴中對(duì)象不一致情況
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }


    //如果存在循環(huán)依賴,則保證最開(kāi)始創(chuàng)建的Bean需要是循環(huán)依賴 getEarlyBeanReference觸發(fā)生成的bean
    //因?yàn)間etEarlyBeanReference 可能返回的是代理類(lèi),因?yàn)閟ingleton必須全局唯一
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        //只有真正存在循環(huán)依賴時(shí),才會(huì)觸發(fā) getEarlyBeanReference調(diào)用產(chǎn)生EarlyBean
        //未存在循環(huán)依賴,則getEarlyBeanReference不觸發(fā),earlySingletonReference為null,返回exposedObject即可
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                ......
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName,
                            "Bean with name '" + beanName + "' has been injected into other beans [" +
                            StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                            "] in its raw version as part of a circular reference, but has eventually been " +
                            "wrapped. This means that said other beans do not use the final version of the " +
                            "bean. This is often the result of over-eager type matching - consider using " +
                            "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }
    return exposedObject;
}

三級(jí)緩存獲取Bean

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    //一級(jí)緩存(單例池)獲取Bean
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //二級(jí)緩存獲取(提前暴露不完全)Bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //三級(jí)緩存Bean的創(chuàng)建工廠獲取bean(可提前被代理)
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

SmartInstantiationAwareBeanPostProcessor重點(diǎn) -> APC之父

//提供提前創(chuàng)建并返回代理的工廠singletonFactory.getObject()執(zhí)行的是個(gè)回調(diào)
//addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));


protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            //getEarlyBeanReference是SmartInstantiationAwareBeanPostProcessor接口定義方法,
            //此方法很關(guān)鍵(構(gòu)造函數(shù)推斷也在此定義)
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}

7、Spring支持動(dòng)態(tài)代理循環(huán)依賴,為何還會(huì)出循環(huán)依賴異常?

(1)、相互依賴的Bean只有需要AOP或者動(dòng)態(tài)代理時(shí)才有可能出現(xiàn)循環(huán)依賴異常

  • 正常情況原始Spring Bean無(wú)論怎樣相互依賴都沒(méi)有問(wèn)題,Spring完全可以處理這種場(chǎng)景
  • 絕大多數(shù)存在AOP場(chǎng)景也都是支持的,Spring支持的
  • 只有相互依賴場(chǎng)景下某些Bean需要被動(dòng)態(tài)代理時(shí)偶爾會(huì)出現(xiàn)循環(huán)依賴異常問(wèn)題,以下解釋異常場(chǎng)景:

通俗解釋(省略很多細(xì)節(jié)):A -> B -> C -> A

  • Spring 啟動(dòng)開(kāi)始創(chuàng)建 A,doCreateBean()中對(duì)A進(jìn)行屬性填充populateBean()時(shí)需要發(fā)現(xiàn)依賴B對(duì)象,此時(shí)A還沒(méi)有進(jìn)行初始化,把A原始對(duì)象包裝成SingletonFactory 放入三級(jí)緩存。
  • A依賴B,因此doCreateBean()會(huì)創(chuàng)建B,并對(duì)B進(jìn)行屬性填空populateBean()時(shí)需要發(fā)現(xiàn)依賴C對(duì)象。
  • C依賴A,因此doCreateBean()會(huì)創(chuàng)建C,并對(duì)C進(jìn)行屬性填空populateBean()時(shí)需要發(fā)現(xiàn)依賴A對(duì)象。
    3.1. 此時(shí)去一級(jí)緩存獲取A,因?yàn)锳前邊并沒(méi)有填充與初始化完成,因此在一級(jí)緩存中不存在;3.2. 去二級(jí)緩存取A,因?yàn)锳前邊并沒(méi)有填充與初始化完成,因此在二級(jí)緩存中不存在;3.3. 去三級(jí)緩存取A,第一步中把A封裝成SingletonFactory放入三級(jí)緩存的,因此三級(jí)緩存中可以獲取到A的對(duì)象3.3.1. 此時(shí)獲取的A如果有必要會(huì)對(duì)A進(jìn)行動(dòng)態(tài)代理,返回代理對(duì)象;3.3.2. 否則不需要代理則返回未填充、未初始化的原始對(duì)象A;
  • 3.4. 獲取到A對(duì)象,注入到C中,接著初始化C,返回C對(duì)象;
  • C對(duì)象返回,注入到B中,接著初始化B,返回B對(duì)象;
  • B對(duì)象返回,注入到A中,接著初始化A,問(wèn)題就在這兒:
    5.1. 如接下來(lái)初始化A無(wú)需被代理5.1.1. exposedObject返回是A原始對(duì)象,此時(shí)與C中被注入A都是原始Bean,完美;
  • 5.2. 如接下來(lái)初始化A需要被代理:
    5.2.1. APC根據(jù)緩存檢查之前創(chuàng)建A時(shí)是否被代理過(guò),如已被代理,直接返回原始對(duì)象,與A原始一致,完美;5.2.2. 但是,如此時(shí)A初始化過(guò)程中有獨(dú)特的其他BeanPostProcessor,對(duì)A的代理方式有單獨(dú)處理,則被代理后的proxy2與原始Bean、被注入到C中的A的Proxy均不再一致,拋出異常;
  • 總結(jié)重點(diǎn):

6.1. 最終原因就是提前暴露的已經(jīng)注入到C中的A(無(wú)論是否被代理)與后來(lái)經(jīng)過(guò)初始化后被代理的A(proxy2)不再是同一個(gè)Bean;

6.2. 因?yàn)镾pring管理Bean默認(rèn)是Singleton的,現(xiàn)在出現(xiàn)了兩個(gè)bean,默認(rèn)情況下無(wú)法決斷,因此就拋出了異常。

(2)、各別注解使用不當(dāng)

  • @Respository
  • 處理器 PersistenceExceptionTranslationPostProcessor#postProcessAfterInitialization
  • 被 @Respository注解的類(lèi)在Spring啟動(dòng)初始化時(shí)存在循環(huán)依賴鏈路中,如果此時(shí)Spring中開(kāi)啟了AOP,則必拋出循環(huán)依賴異常
  • 所以DAO層使用時(shí),最好不要引入外部業(yè)務(wù)邏輯,業(yè)務(wù)邏輯可以提取到Manager、Service層等中,保持DAO純凈
  • 案例分析:見(jiàn)第四節(jié)
  • @Asyn
  • 處理器 AsyncAnnotationBeanPostProcessor#postProcessAfterInitialization
  • 被 @Asyn注解的類(lèi)在Spring啟動(dòng)初始化時(shí)存在循環(huán)依賴鏈路中,如果此時(shí)Spring中開(kāi)啟了AOP,則必拋出循環(huán)依賴異常
  • 以上等注解的類(lèi)使用不當(dāng)都比較容易出現(xiàn)循環(huán)依賴,這兩個(gè)注解同一個(gè)父類(lèi),造成循環(huán)依賴原理一樣
    AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization

(3)、存在多個(gè)AutoProxyCreator(APC),出現(xiàn)多層代理

spring默認(rèn)保證一個(gè)容器中只能有一個(gè)Aop的APC,如過(guò)手動(dòng)添加或者自定義會(huì)出現(xiàn)多個(gè)APC情況

  • InfrastructureAdvisorAutoProxyCreator
  • AspectJAwareAdvisorAutoProxyCreator
  • AnnotationAwareAspectJAutoProxyCreator

三者有就按照優(yōu)先級(jí)覆蓋,否則就注冊(cè)一個(gè),因此始終就只會(huì)有一個(gè)APC

AopConfigUtils

static {

    APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);

    APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);

    APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);

}




private static BeanDefinition registerOrEscalateApcAsRequired(

            Class cls, BeanDefinitionRegistry registry, @Nullable Object source) {




    

    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {

        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);

        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {

            //因?yàn)槿齻€(gè)APC存在能力父子關(guān)系,按照指定注冊(cè)的APC自動(dòng)調(diào)整優(yōu)先級(jí),從而保證只存在一個(gè)APC

            //如未指定APC,則默認(rèn)為InfrastructureAdvisorAutoProxyCreator

            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());

            int requiredPriority = findPriorityForClass(cls);

            if (currentPriority < requiredPriority) {

                apcDefinition.setBeanClassName(cls.getName());

            }

        }

        return null;

    }




    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);

    beanDefinition.setSource(source);

    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);

    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);

    return beanDefinition;

}

存在多個(gè)APC時(shí),如存在循環(huán)依賴,此時(shí)觸發(fā)之前放入三級(jí)緩存邏輯

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

從而觸發(fā)多個(gè)APC的 getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

    Object exposedObject = bean;

    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {

        //此時(shí)如存在多個(gè)APC,則依次執(zhí)行 getEarlyBeanReference 返回多層代理對(duì)象

        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {

            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);

        }

    }

    return exposedObject;

}


最終proxy2會(huì)被注入到依賴的Bean中,即例如:A-proxy2 注入到 B中

存在多個(gè)多層代理情況,getEarlyBeanReference 沒(méi)有問(wèn)題,但是執(zhí)行到初始化時(shí)

@Override

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {

    if (bean != null) {

        //注意這個(gè)Bean可是原始對(duì)象,每個(gè)APC都緩存自身代理過(guò)的類(lèi),但是存在多個(gè)APC時(shí),后續(xù)的APC緩存的確是代理類(lèi)的代理

        //即如第二個(gè)APC是BeanNameAutoProxyCreator,其緩存的可是 proxy1的class,原始類(lèi)在此APC是沒(méi)被代理過(guò)的,

        //因此此時(shí)會(huì)對(duì)原始類(lèi)進(jìn)行二次代理,產(chǎn)生Proxy3

        Object cacheKey = getCacheKey(bean.getClass(), beanName);

        if (this.earlyProxyReferences.remove(cacheKey) != bean) {

            return wrapIfNecessary(bean, beanName, cacheKey);

        }

    }

    return bean;

}







//視線返回本次循環(huán)依賴最初實(shí)例化的結(jié)點(diǎn):A->B->C->A,則此處為A的創(chuàng)建流程

//此時(shí)A 通過(guò) getEarlyBeanReference生成A ->proxy2注入到C中,

//C直接實(shí)例創(chuàng)建不會(huì)觸發(fā)getEarlyBeanReference,注入到B中

//B直接實(shí)例創(chuàng)建不會(huì)觸發(fā)getEarlyBeanReference,注入到A中

//A依賴處理完畢,繼續(xù)初始化 initializeBean流程 -> postProcessAfterInitialization,返回 proxy3

if (earlySingletonExposure) {

    //此時(shí)獲取到的代理類(lèi)是 proxy2,即已經(jīng)注入到依賴類(lèi)C中的代理,因此不為null

    Object earlySingletonReference = getSingleton(beanName, false);

    if (earlySingletonReference != null) {

        //多APC時(shí),exposedObject 在之前initializeBean -> postProcessAfterInitialization作用下返回proxy3

        //proxy3 != bean 不一致,違反了singletion原則,因此會(huì)拋出循環(huán)依賴異常

        if (exposedObject == bean) {

            exposedObject = earlySingletonReference;

        }

        else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {

            ......

            if (!actualDependentBeans.isEmpty()) {

                throw new BeanCurrentlyInCreationException(beanName,

                                                           "Bean with name '" + beanName + "' has been injected into other beans [" +

                                                           StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +

                                                           "] in its raw version as part of a circular reference, but has eventually been " +

                                                           "wrapped. This means that said other beans do not use the final version of the " +

                                                           "bean. This is often the result of over-eager type matching - consider using " +

                                                           "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");

            }

        }

    }

}

8、正常AOP代理為何沒(méi)問(wèn)

SmartInstantiationAwareBeanPostProcessor

@Override

public Object getEarlyBeanReference(Object bean, String beanName) {

    Object cacheKey = getCacheKey(bean.getClass(), beanName);

    this.earlyProxyReferences.put(cacheKey, bean);

    return wrapIfNecessary(bean, beanName, cacheKey);

}







//提前通過(guò)singletonFactory.getObject()創(chuàng)建的代理緩存起來(lái)以后,這里如果再次判斷需要代理,

//緩存中存在已被代理則直接返回原始bean,無(wú)需再次代理,后續(xù)直接獲取earlySingletonReference,

//因此前后代理出來(lái)的對(duì)象是一致的

@Override

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {

    if (bean != null) {

        Object cacheKey = getCacheKey(bean.getClass(), beanName);

        if (this.earlyProxyReferences.remove(cacheKey) != bean) {

            return wrapIfNecessary(bean, beanName, cacheKey);

        }

    }

    return bean;

}

解決方案

1、無(wú)需代理場(chǎng)景使用原始對(duì)象

  • 原始對(duì)象相互注入沒(méi)有問(wèn)題,檢查不許要生成代理的類(lèi)

2、@lazy解耦

  • 原理是發(fā)現(xiàn)有@lazy注解的依賴為其生成代理類(lèi),依賴代理類(lèi),從而實(shí)現(xiàn)了解耦
  • @Lazy 用來(lái)標(biāo)識(shí)類(lèi)是否需要延遲加載;
  • @Lazy 可以作用在類(lèi)上、方法上、構(gòu)造器上、方法參數(shù)上、成員變量中;
  • @Lazy 作用于類(lèi)上時(shí),通常與 @Component 及其衍生注解配合使用;
  • @Lazy 注解作用于方法上時(shí),通常與 @Bean 注解配合使用;

DefaultListableBeanFactory#resolveDependency

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                                @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {


    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    if (Optional.class == descriptor.getDependencyType()) {
        return createOptionalDependency(descriptor, requestingBeanName);
    }
    ......
    else {
        //處理@lazy
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
            descriptor, requestingBeanName);
        if (result == null) {
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}


ContextAnnotationAutowireCandidateResolver#getLazyResolutionProxyIfNecessary
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
    return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}


ContextAnnotationAutowireCandidateResolver#isLazy
//是否為@lazy,如果為@lazy則創(chuàng)建依賴代理
protected boolean isLazy(DependencyDescriptor descriptor) {
    for (Annotation ann : descriptor.getAnnotations()) {
        Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
        if (lazy != null && lazy.value()) {
            return true;
        }
    }
    .......
}

3、抽取公共邏輯

  • 業(yè)務(wù)層面重構(gòu),不再相互依賴而是依賴公共模塊,并且各個(gè)對(duì)外業(yè)務(wù)與內(nèi)部接口拆分

案例(可直接運(yùn)行)

1、@Repository案例分析

import org.junit.Test;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
/**
 * @author: Superizer
 */
@Component
public class MainSpringCircularDependencyTester
{
    @Test
    public void springCircularDependencyTest()
    {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringCircularDependencyConfig.class);
        X x = ac.getBean(X.class);
        System.out.println("Spring bean X =" + x.getClass().getName());
        x.display();
        Y y = ac.getBean(Y.class);
        System.out.println("Spring bean Y =" + y.getClass().getName());
        y.display();
        Z z = ac.getBean(Z.class);
        System.out.println("Spring bean Z =" + z.getClass().getName());
        z.display();
        System.out.println("******************Main********************");
    }
    @Configuration
    @ComponentScan("com.myself.demo.spring.v5.circular.dependency")
//  @EnableAspectJAutoProxy
    @ConditionalOnClass(PersistenceExceptionTranslationPostProcessor.class)
    static class SpringCircularDependencyConfig{
        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnProperty(prefix = "spring.dao.exceptiontranslation", name = "enabled",
                matchIfMissing = true)
        public static PersistenceExceptionTranslationPostProcessor
        persistenceExceptionTranslationPostProcessor(Environment environment) {
            PersistenceExceptionTranslationPostProcessor postProcessor = new PersistenceExceptionTranslationPostProcessor();
            boolean proxyTargetClass = environment.getProperty(
                    "spring.aop.proxy-target-class", Boolean.class, Boolean.TRUE);
            postProcessor.setProxyTargetClass(proxyTargetClass);
            return postProcessor;
        }
    }
    abstract static class A {
        public abstract A injectSources();
        public abstract A self();
        public void display(){
            System.out.println("injectSources:" + injectSources().getClass().getName());
            System.out.println("*******************************************************");
        }
    }
    //X、Y、Z 只要循環(huán)依賴中第一個(gè)類(lèi)X有注解@Repository,就會(huì)出現(xiàn)循環(huán)依賴異常
    //執(zhí)行X的singletonFactory.getObject()返回的原對(duì)象,但是后邊初始化時(shí)
    //執(zhí)行到PersistenceExceptionTranslationPostProcessor時(shí)單獨(dú)創(chuàng)建代理邏輯返回的是代理類(lèi)
    //exposedObject = initializeBean(beanName, exposedObject, mbd);
    @Repository
//  @Component
    static class X  extends A{
        @Resource
        private Y y;
        @Override
        public Y injectSources()
        {
            return y;
        }
        @Override
        public X self() {
            return this;
        }
    }
    @Component
//  @Repository
    static class Y extends A{
        @Resource
        private Z z;
        @Override
        public Z injectSources() {
            return z;
        }
        @Override
        public Y self()
        {
            return this;
        }
    }
    @Component
//  @Repository
    static class Z extends A{
        @Resource
        private X x;
        @Override
        public X injectSources()
        {
            return x;
        }
        @Override
        public Z self()
        {
            return this;
        }
    }
}

2、多AutoProxyCreator場(chǎng)景

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.aop.support.AbstractExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
/**
 * @author: Superizer
 * Copyright (C) 2021
 * All rights reserved
 */
@Component
public class MainSpringCircularDependencyV2Tester
{
    @Test
    public void circularDependencyV2Tester()
    {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringCircularDependencyConfig.class);
        A a = ac.getBean(A.class);
        System.out.println("Spring bean A =" + a.getClass().getName());
        a.display();
        B y = ac.getBean(B.class);
        System.out.println("Spring bean B =" + y.getClass().getName());
        y.display();
        C z = ac.getBean(C.class);
        System.out.println("Spring bean C =" + z.getClass().getName());
        z.display();
        System.out.println("******************Main********************");
    }
    @Configuration
    @ComponentScan("com.myself.demo.spring.v5.circular.dependency.v2")
    @EnableAspectJAutoProxy
    static class SpringCircularDependencyConfig {
        @Bean
        public DefaultPointcutAdvisor defaultPointcutAdvisor() {
            DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
            Pointcut pointcut = new AbstractExpressionPointcut() {
                @Override
                public ClassFilter getClassFilter() {
                    return (tmp) -> {
                        String name = tmp.getName();
                        if(name.equals(A.class.getName())) {
                            return true;
                        }
                        return false;
                    };
                }
                @Override
                public MethodMatcher getMethodMatcher() {
                    return MethodMatcher.TRUE;
                }
            };
            advisor.setPointcut(pointcut);
            advisor.setAdvice(new SpringAopAroundMethod());
            advisor.setOrder(0);
            return advisor;
        }
        @Bean
        public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
            BeanNameAutoProxyCreator apc = new BeanNameAutoProxyCreator();
            apc.setBeanNames("a");
            apc.setOrder(-1);
            apc.setProxyTargetClass(true);
            return apc;
        }
    }
    abstract static class G {
        public abstract G injectSources();
        public abstract G self();
        public void display(){
            System.out.println("injectSources:" + injectSources().getClass().getName());
            System.out.println("*******************************************************");
        }
    }
    @Component(value = "a")
    static class A  extends G {
        @Resource
        private B b;
        @Override
        public B injectSources()
        {
            return b;
        }
        @Override
        public A self() {
            return this;
        }
    }
    @Component
    static class B extends G {
        @Resource
        private C c;
        @Override
        public C injectSources() {
            return c;
        }
        @Override
        public B self()
        {
            return this;
        }
    }
    @Component
    static class C extends G {
        @Resource
        private A a;
        @Override
        public A injectSources()
        {
            return a;
        }
        @Override
        public C self()
        {
            return this;
        }
    }
    static class SpringAopAroundMethod implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            System.out.println("Aop Before method!");
            try {
                Object result = methodInvocation.proceed();
                System.out.println("Aop after method!");
                return result;
            } catch (IllegalArgumentException e) {
                System.out.println("Aop throw exception!");
                throw e;
            }
        }
    }
}

總結(jié)

出現(xiàn)循環(huán)依賴其實(shí)反映代碼結(jié)構(gòu)設(shè)計(jì)上的問(wèn)題,理論上應(yīng)當(dāng)將循環(huán)依賴進(jìn)行分層,抽取公共部分,然后由各個(gè)功能類(lèi)再去依賴公共部分。

但是在復(fù)雜代碼中,各個(gè)service、manager類(lèi)互相調(diào)用太多,總會(huì)一不小心出現(xiàn)一些類(lèi)之間的循環(huán)依賴的問(wèn)題??捎袝r(shí)候我們又發(fā)現(xiàn)在用Spring進(jìn)行依賴注入時(shí),雖然Bean之間有循環(huán)依賴,但是代碼本身卻大概率能很正常的work,似乎也沒(méi)有任何bug。

很多敏感的同學(xué)心里肯定有些犯嘀咕,循環(huán)依賴這種觸犯因果律的事情怎么能發(fā)生呢?沒(méi)錯(cuò),這一切其實(shí)都并不是那么理所當(dāng)然。Spring已經(jīng)為我們背負(fù)了太多,但絕不是偷懶的借口,還是應(yīng)該規(guī)范設(shè)計(jì),規(guī)范代碼,盡量做到從根本上避免這種循環(huán)依賴的發(fā)生。

Spring流程圖


網(wǎng)站欄目:Spring循環(huán)依賴那些事兒(含Spring詳細(xì)流程圖)
本文URL:http://m.5511xx.com/article/djgejge.html