精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

萬字長文帶你徹底吃透Spring循環依賴,堪稱全網最全

開發 前端
本章,主要詳細分析了Spring的循環依賴問題,首先介紹了緩存依賴的基本概念和循環依賴的類型。隨后以案例的形式詳細介紹了循環依賴的場景,并詳細分析了Spring循環依賴的底層解決方案。

一、學習指引

Spring中的循環依賴問題,你真的徹底了解過嗎?

Spring的循環依賴問題可以說是面試過程中出現的非常頻繁的問題。比如,在面試過程中面試官可能會問:有了解過Spring中的循環依賴問題嗎?或者會問:什么是循環依賴問題?或者會問:Spring中在哪些場景下會產生循環依賴問題?或者會問:Spring是如何解決循環依賴問題的?看似輕描淡寫的一句話,實際要考察的內容也是比較多的。面試官可以通過這個問題考察面試者是否研究過Spring的源碼,有沒有了解過Spring中的循環依賴問題。

文末掃碼加星球立減¥200,僅限2023年12月,僅限前300名,先到先得。

本章,我們就一起來聊聊Spring中的循環依賴問題。

二、循環依賴概述

什么是循環依賴?

循環依賴其實也很好理解,可以將這個詞拆分成兩部分,一個是循環,一個是依賴。循環,顧名思義就是指形成了一個閉合環路,也就是閉環。依賴就是指某個事件的發生要依賴另一個事件。

在Spring中的循環依賴就是指一個或者多個Bean之間存在著互相依賴的關系,并且形成了循環調用。例如,在Spring中,A依賴B,B又依賴A,A和B之間就形成了相互依賴的關系。創建A對象時,發現A對象依賴了B對象,此時先去創建B對象。創建B對象時,發現B對象又依賴了A對象,此時又去創建A對象。創建A對象時,發現A對象依賴了B對象....如果Spring不去處理這種情況,就會發生死循環,一直會創建A對象和B對象,直到拋出異常為止。

同理,在Spring中多個對象之間也有可能存在循環依賴,例如,A依賴B,B依賴C,C又依賴A,A、B、C之間形成了互相依賴的關系,這也是一種循環依賴。

三、循環依賴類型

循環依賴有這些類型呢?

循環依賴總體上可以分成:自我依賴、直接依賴和間接依賴三種類型,如圖20-1所示。

圖片圖片

3.1 自我依賴

自我依賴就是自己依賴自己,從而形成的循環依賴,一般情況下不會發生這種循環依賴,如圖20-2所示。

圖片圖片

3.2 直接依賴

直接依賴一般是發生在兩個對象之間,例如對象A依賴對象B,對象B又依賴對象A,對象A和對象B之間形成了依賴關系,如圖20-3所示。

圖片圖片

3.3 間接依賴

間接依賴一般是發生在三個或三個以上對象之間互相依賴的場景,例如對象A依賴對象B,對象B依賴對象C,對象C又依賴對象A,對象A、對象B和對象C之間就形成了循環依賴,如圖20-4所示。

圖片圖片

四、循環依賴場景

Spring中有哪些循環依賴的場景?

Spring中的循環依賴場景總體上可以分成單例Bean的setter循環依賴、多例Bean的setter循環依賴、代理對象的setter循環依賴、構造方法的循環依賴和DependsOn的循環依賴。如圖20-5所示。

圖片圖片

4.1 單例Bean的setter循環依賴的特殊情況

Spring是支持基于單例Bean的setter方法的循環依賴的,不過有一種特殊情況需要注意。本節,我們就一起實現基于單例Bean的setter方法的循環依賴的特殊情況,具體實現步驟如下所示。

(1)新增SpecialCircularBeanA類

SpecialCircularBeanA類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.bean.SpecialCircularBeanA。

@Component
public class SpecialCircularBeanA {
    @Autowired
    private SpecialCircularBeanB specialCircularBeanB;
    @Override
    public String toString() {
        return "SpecialCircularBeanA{" +
                "specialCircularBeanB=" + specialCircularBeanB +
                '}';
    }
}

可以看到,在SpecialCircularBeanA類上只標注了@Component注解,所以在IOC容器啟動時,會在IOC容器中創建SpecialCircularBeanA類型的單例Bean,并且在SpecialCircularBeanA類型的Bean對象中,會依賴SpecialCircularBeanB類型的Bean對象。同時,在SpecialCircularBeanA類中重寫了toString()方法,打印了依賴的SpecialCircularBeanB類型的對象。

(2)新增SpecialCircularBeanB類

SpecialCircularBeanB類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.bean.SpecialCircularBeanB。

@Component
public class SpecialCircularBeanB {
    @Autowired
    private SpecialCircularBeanA specialCircularBeanA;
    @Override
    public String toString() {
        return "SpecialCircularBeanB{" +
                "specialCircularBeanA=" + specialCircularBeanA +
                '}';
    }
}

可以看到,在SpecialCircularBeanB類上只標注了@Component注解,所以在IOC容器啟動時,會在IOC容器中創建SpecialCircularBeanB類型的單例Bean,并且在SpecialCircularBeanB類型的Bean對象中,會依賴SpecialCircularBeanA類型的Bean對象。同時,在SpecialCircularBeanB類中重寫了toString()方法,打印了依賴的SpecialCircularBeanA類型的對象。

(3)新增SpecialCircularConfig類

SpecialCircularConfig類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.config.SpecialCircularConfig。

@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.special"})
public class SpecialCircularConfig {
}

可以看到,在SpecialCircularConfig類上標注了@Configuration注解,說明SpecialCircularConfig類是案例程序的配置類,并且使用@ComponentScan注解指定了掃描的包。

(4)新增SpecialCircularTest類

SpecialCircularTest類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.special.SpecialCircularTest。

public class SpecialCircularTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpecialCircularConfig.class);
        SpecialCircularBeanA specialCircularBeanA = context.getBean(SpecialCircularBeanA.class);
        System.out.println(specialCircularBeanA);
        context.close();
    }
}

可以看到,在SpecialCircularTest類的main()方法中,傳入SpecialCircularConfig類的Class對象后,創建IOC容器,隨后從IOC容器中獲取SpecialCircularBeanA類型的Bean對象并進行打印,最后關閉IOC容器。

(5)運行SpecialCircularTest類

運行SpecialCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" java.lang.StackOverflowError

可以看到,程序拋出了StackOverflowError異常。

其實,從本質上講,這個異常不是Spring拋出的,而是JVM拋出的棧溢出錯誤。

4.2 多例Bean的setter循環依賴

Spring是不支持基于多例Bean,也就是原型模式下的Bean的setter方法的循環依賴。本節,我們一起實現一個基于多例Bean的set方法的循環依賴案例,具體實現步驟如下所示。

(1)新增PrototypeCircularBeanA類

PrototypeCircularBeanA類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.bean.PrototypeCircularBeanA。

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeCircularBeanA {
    @Autowired
    private PrototypeCircularBeanB prototypeCircularBeanB;

    public PrototypeCircularBeanB getPrototypeCircularBeanB() {
        return prototypeCircularBeanB;
    }
}

可以看到,PrototypeCircularBeanA類的對象在Spring中是多例Bean,并且依賴了PrototypeCircularBeanB類型的Bean對象,并提供了getPrototypeCircularBeanB()方法返回PrototypeCircularBeanB類型的Bean對象。

(2)新增PrototypeCircularBeanB類

PrototypeCircularBeanB類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.bean.PrototypeCircularBeanB。

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeCircularBeanB {
    @Autowired
    private PrototypeCircularBeanA prototypeCircularBeanA;
    
    public PrototypeCircularBeanA getPrototypeCircularBeanA() {
        return prototypeCircularBeanA;
    }
}

可以看到,PrototypeCircularBeanB類的對象在Spring中是多例Bean,并且依賴了PrototypeCircularBeanA類型的Bean對象,并提供了getPrototypeCircularBeanA()方法返回PrototypeCircularBeanA類型的Bean對象。

(3)新增PrototypeCircularConfig類

PrototypeCircularConfig類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.config.PrototypeCircularConfig。

@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.prototype"})
public class PrototypeCircularConfig {
}

可以看到,在PrototypeCircularConfig類上標注了@Configuration注解,說明PrototypeCircularConfig類是案例程序的配置類,并且使用@ComponentScan注解指定了要掃描的包名。

(4)新增PrototypeCircularTest類

PrototypeCircularTest類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.prototype.PrototypeCircularTest。

public class PrototypeCircularTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PrototypeCircularConfig.class);
        PrototypeCircularBeanA prototypeCircularBeanA = context.getBean(PrototypeCircularBeanA.class);
        System.out.println("prototypeCircularBeanA===>>>" + prototypeCircularBeanA);
        System.out.println("prototypeCircularBeanB===>>>" + prototypeCircularBeanA.getPrototypeCircularBeanB());
        context.close();
    }
}

可以看到,在PrototypeCircularTest類的main()方法中,創建完IOC容器后,會從IOC容器中獲取PrototypeCircularBeanA類型的Bean對象,并打印PrototypeCircularBeanA類型的Bean對象和依賴的PrototypeCircularBeanB類型的Bean對象。

(5)運行PrototypeCircularTest類

運行PrototypeCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'prototypeCircularBeanA': Unsatisfied dependency expressed through field 'prototypeCircularBeanB': Error creating bean with name 'prototypeCircularBeanB': Unsatisfied dependency expressed through field 'prototypeCircularBeanA': Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/***************省略其他輸出信息**************/
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'prototypeCircularBeanB': Unsatisfied dependency expressed through field 'prototypeCircularBeanA': Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/***************省略其他輸出信息**************/
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/***************省略其他輸出信息**************/

可以看到,輸出結果中打印出了循環依賴的異常信息,說明Spring不支持多例Bean的setter方法循環依賴。

4.3 代理對象的setter循環依賴

Spring默認是不支持基于代理對象的setter方法的循環依賴,本節,就簡單實現一個基于代理對象的setter方法的循環依賴案例。具體實現步驟如下所示。

(1)新增ProxyCircularBeanA類

ProxyCircularBeanA類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.bean.ProxyCircularBeanA。

@Component
public class ProxyCircularBeanA {
    @Autowired
    private ProxyCircularBeanB proxyCircularBeanB;

    @Async
    public ProxyCircularBeanB getProxyCircularBeanB() {
        return proxyCircularBeanB;
    }
}

可以看到,ProxyCircularBeanA類型的Bean對象會依賴ProxyCircularBeanB類型的Bean對象,并且在getProxyCircularBeanB()方法上標注了@Async注解,當調用getProxyCircularBeanB()方法時,會通過AOP自動生成代理對象。

(2)新增ProxyCircularBeanB類

ProxyCircularBeanB類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.bean.ProxyCircularBeanB。

@Component
public class ProxyCircularBeanB {
    @Autowired
    private ProxyCircularBeanA proxyCircularBeanA;

    @Async
    public ProxyCircularBeanA getProxyCircularBeanA() {
        return proxyCircularBeanA;
    }
}

可以看到,ProxyCircularBeanB類型的Bean對象會依賴ProxyCircularBeanA類型的Bean對象,并且在getProxyCircularBeanA()方法上標注了@Async注解,當調用getProxyCircularBeanA()方法時,會通過AOP自動生成代理對象。

(3)新增ProxyCircularConfig類

ProxyCircularConfig類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.config.ProxyCircularConfig。

@EnableAsync
@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.proxy"})
public class ProxyCircularConfig {
}

可以看到,在ProxyCircularConfig類上標注了@Configuration注解,說明ProxyCircularConfig類是案例程序的配置類。并且在ProxyCircularConfig類上使用@ComponentScan注解指定了要掃描的包名。同時,使用@EnableAsync注解開啟了異步調用。

(4)新增ProxyCircularTest類

ProxyCircularTest類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.proxy.ProxyCircularTest。

public class ProxyCircularTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProxyCircularConfig.class);
        ProxyCircularBeanA proxyCircularBeanA = context.getBean(ProxyCircularBeanA.class);
        System.out.println("proxyCircularBeanA===>>>" + proxyCircularBeanA);
        System.out.println("proxyCircularBeanB===>>>" + proxyCircularBeanA.getProxyCircularBeanB());
        context.close();
    }
}

可以看到,在ProxyCircularTest類的main()方法中,創建完IOC容器后,會從IOC容器中獲取ProxyCircularBeanA類型的Bean對象,并打印ProxyCircularBeanA類型的Bean對象和依賴的ProxyCircularBeanB類型的Bean對象。

(5)運行ProxyCircularTest類

運行ProxyCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'proxyCircularBeanA': Bean with name 'proxyCircularBeanA' has been injected into other beans [proxyCircularBeanB] 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.

從輸出的結果信息可以看出,Spring拋出了循環依賴的異常。說明Spring默認不支持基于代理對象的setter方法的循環依賴。

4.4 構造方法的循環依賴

Spring不支持基于構造方法的循環依賴。本節,就簡單實現一個基于構造方法的循環依賴,具體實現步驟如下所示。

(1)新增ConstructCircularBeanA類

ConstructCircularBeanA類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.bean.ConstructCircularBeanA。

@Component
public class ConstructCircularBeanA {
    
    private ConstructCircularBeanB constructCircularBeanB;

    private ConstructCircularBeanA(ConstructCircularBeanB constructCircularBeanB){
        this.constructCircularBeanB = constructCircularBeanB;
    }

    public ConstructCircularBeanB getConstructCircularBeanB() {
        return constructCircularBeanB;
    }
}

可以看到,在ConstructCircularBeanA類中通過構造方法依賴了ConstructCircularBeanB類型的Bean對象,并提供了getConstructCircularBeanB()方法來獲取ConstructCircularBeanB類型的Bean對象。

(2)新增ConstructCircularBeanB類

ConstructCircularBeanB類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.bean.ConstructCircularBeanB。

@Component
public class ConstructCircularBeanB {

    private ConstructCircularBeanA constructCircularBeanA;

    public ConstructCircularBeanB(ConstructCircularBeanA constructCircularBeanA) {
        this.constructCircularBeanA = constructCircularBeanA;
    }

    public ConstructCircularBeanA getConstructCircularBeanA() {
        return constructCircularBeanA;
    }
}

可以看到,在ConstructCircularBeanB類中通過構造方法依賴了ConstructCircularBeanA類型的Bean對象,并提供了getConstructCircularBeanA()方法來獲取ConstructCircularBeanA類型的Bean對象。

(3)新增ConstructCircularConfig類

ConstructCircularConfig類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.config.ConstructCircularConfig。

@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.construct"})
public class ConstructCircularConfig {
}

可以看到,在ConstructCircularConfig類上標注了@Configuration注解,說明ConstructCircularConfig類是案例程序的配置類,并且使用@ComponentScan注解指定了要掃描的包名。

(4)新增ConstructCircularTest類

ConstructCircularTest類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.construct.ConstructCircularTest。

public class ConstructCircularTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConstructCircularConfig.class);
        ConstructCircularBeanA constructCircularBeanA = context.getBean(ConstructCircularBeanA.class);
        System.out.println("cnotallow===>>>" + constructCircularBeanA);
        System.out.println("cnotallow===>>>" + constructCircularBeanA.getConstructCircularBeanB());
        context.close();
    }
}

可以看到,在ConstructCircularTest類的main()方法中,創建完IOC容器后,會從IOC容器中獲取ConstructCircularBeanA類型的Bean對象,并打印ConstructCircularBeanA類型的Bean對象和依賴的ConstructCircularBeanB類型的Bean對象。

(5)運行ConstructCircularTest類

運行ConstructCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'constructCircularBeanA' defined in file [D:\Workspaces\2022\spring\spring-annotation-book\spring-annotation\spring-annotation-chapter-20\target\classes\io\binghe\spring\annotation\chapter20\construct\bean\ConstructCircularBeanA.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanB' defined in file [D:\Workspaces\2022\spring\spring-annotation-book\spring-annotation\spring-annotation-chapter-20\target\classes\io\binghe\spring\annotation\chapter20\construct\bean\ConstructCircularBeanB.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/*************省略其他信息**************/
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'constructCircularBeanB' defined in file [D:\Workspaces\2022\spring\spring-annotation-book\spring-annotation\spring-annotation-chapter-20\target\classes\io\binghe\spring\annotation\chapter20\construct\bean\ConstructCircularBeanB.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/*************省略其他信息**************/
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

可以看到,Spring拋出了循環依賴的異常,說明Spring不支持基于構造方法的循環依賴。

4.5 @DependsOn的循環依賴

@DependsOn注解主要用于指定Bean的實例化順序,Spring默認是不支持基于@DependsOn注解的循環依賴。本節,就實現基于@DependsOn注解的循環依賴。具體實現步驟如下所示。

(1)新增DependsOnCircularBeanA類

DependsOnCircularBeanA類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.bean.DependsOnCircularBeanA。

@Component
@DependsOn("dependsOnCircularBeanB")
public class DependsOnCircularBeanA {
    @Autowired
    private DependsOnCircularBeanB dependsOnCircularBeanB;

    public DependsOnCircularBeanB getDependsOnCircularBeanB() {
        return dependsOnCircularBeanB;
    }
}

可以看到,在DependsOnCircularBeanA類上不僅標注了@Component注解,也標注了@DependsOn注解指定依賴dependsOnCircularBeanB,并且在DependsOnCircularBeanA類中使用@Autowired注解注入了DependsOnCircularBeanB類型的Bean對象。同時,在DependsOnCircularBeanA類中提供了getDependsOnCircularBeanB()方法獲取DependsOnCircularBeanB類型的Bean對象。

(2)新增DependsOnCircularBeanB類

DependsOnCircularBeanB類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.bean.DependsOnCircularBeanB。

@Component
@DependsOn("dependsOnCircularBeanA")
public class DependsOnCircularBeanB {
    @Autowired
    private DependsOnCircularBeanA dependsOnCircularBeanA;

    public DependsOnCircularBeanA getDependsOnCircularBeanA() {
        return dependsOnCircularBeanA;
    }
}

可以看到,在DependsOnCircularBeanB類上不僅標注了@Component注解,也標注了@DependsOn注解指定依賴dependsOnCircularBeanA,并且在DependsOnCircularBeanB類中使用@Autowired注解注入了DependsOnCircularBeanA類型的Bean對象。同時,在DependsOnCircularBeanB類中提供了getDependsOnCircularBeanA()方法獲取DependsOnCircularBeanA類型的Bean對象。

(3)新增DependsOnCircularConfig類

DependsOnCircularConfig類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.config.DependsOnCircularConfig。

@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.dependson"})
public class DependsOnCircularConfig {
}

可以看到,在DependsOnCircularConfig類上標注了@Configuration注解,說明DependsOnCircularConfig類是案例程序的配置類。同時,使用@ComponentScan注解指定了要掃描的包名。

(4)新增DependsOnCircularTest類

DependsOnCircularTest類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.dependson.DependsOnCircularTest。

public class DependsOnCircularTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DependsOnCircularConfig.class);
        DependsOnCircularBeanA dependsOnCircularBeanA = context.getBean(DependsOnCircularBeanA.class);
        System.out.println("dependsOnCircularBeanA===>>>" + dependsOnCircularBeanA);
        System.out.println("dependsOnCircularBeanB===>>>" + dependsOnCircularBeanA.getDependsOnCircularBeanB());
        context.close();
    }
}

可以看到,在DependsOnCircularTest類的main()方法中,創建完IOC容器后,會從IOC容器中獲取DependsOnCircularBeanA類型的Bean對象,并打印DependsOnCircularBeanA類型的Bean對象和依賴的DependsOnCircularBeanB類型的Bean對象。

(5)運行DependsOnCircularTest類

運行DependsOnCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dependsOnCircularBeanB' defined in file [D:\Workspaces\2022\spring\spring-annotation-book\spring-annotation\spring-annotation-chapter-20\target\classes\io\binghe\spring\annotation\chapter20\dependson\bean\DependsOnCircularBeanB.class]: Circular depends-on relationship between 'dependsOnCircularBeanB' and 'dependsOnCircularBeanA'

從輸出的結果信息可以看出,Spring拋出了循環依賴的異常。說明Spring不支持@DependsOn注解的循環依賴。

4.6 單例Bean的setter循環依賴

Spring支持基于單例Bean的setter方法的循環依賴。本節,就實現基于單例Bean的setter方法的循環依賴案例。具體實現步驟如下所示。

(1)新增SingletonCircularBeanA類

SingletonCircularBeanA類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanA。

@Component
public class SingletonCircularBeanA {
    @Autowired
    private SingletonCircularBeanB singletonCircularBeanB;

    public SingletonCircularBeanB getSingletonCircularBeanB() {
        return singletonCircularBeanB;
    }
}

可以看到,在SingletonCircularBeanA類中依賴了SingletonCircularBeanB類型的Bean對象,并提供了getSingletonCircularBeanB()方法獲取SingletonCircularBeanB類型的Bean對象。

(2)新增SingletonCircularBeanB類

SingletonCircularBeanB類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanB。

@Component
public class SingletonCircularBeanB {
    @Autowired
    private SingletonCircularBeanA singletonCircularBeanA;

    public SingletonCircularBeanA getSingletonCircularBeanA() {
        return singletonCircularBeanA;
    }
}

可以看到,在SingletonCircularBeanB類中依賴了SingletonCircularBeanA類型的Bean對象,并提供了getSingletonCircularBeanA()方法獲取SingletonCircularBeanA類型的Bean對象。

(3)新增SingletonCircularConfig類

SingletonCircularConfig類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.config.SingletonCircularConfig。

@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter20.singleton"})
public class SingletonCircularConfig {
}

可以看到,在SingletonCircularConfig類上標注了@Configuration注解,說明SingletonCircularConfig類是案例程序的配置類,并使用@ComponentScan注解指定了要掃描的包名。

(4)新增SingletonCircularTest類

SingletonCircularTest類的源碼詳見:spring-annotation-chapter-20工程下的io.binghe.spring.annotation.chapter20.singleton.SingletonCircularTest。

public class SingletonCircularTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SingletonCircularConfig.class);
        SingletonCircularBeanA singletonCircularBeanA = context.getBean(SingletonCircularBeanA.class);
        System.out.println("singletnotallow===>>>" + singletonCircularBeanA);
        System.out.println("singletnotallow===>>>" + singletonCircularBeanA.getSingletonCircularBeanB());
        context.close();
    }
}

可以看到,在SingletonCircularTest類的main()方法中,創建完IOC容器后,會從IOC容器中獲取SingletonCircularBeanA類型的Bean對象,并打印SingletonCircularBeanA類型的Bean對象和依賴的SingletonCircularBeanB類型的Bean對象。

(5)運行SingletonCircularTest類

運行SingletonCircularTest類的main()方法,輸出的結果信息如下所示。

singletnotallow===>>>io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanA@41e1e210
singletnotallow===>>>io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanB@be35cd9

可以看到,正確輸出了SingletonCircularBeanA類型的Bean對象和SingletonCircularBeanB類型的Bean對象。**說明Spring支持基于單例Bean的setter方法的循環依賴。

五、Spring循環依賴底層解決方案分析

Spring底層是如何解決循環依賴問題的?

由循環依賴場景的分析得知:Spring除了默認支持基于單例Bean的setter方法的循環依賴外,默認均不支持其他情況下的循環依賴。但是,如果是通過標注@Async注解生成的代理對象,則可以通過將標注了@Async注解的類排到后面加載的IOC容器中即可解決循環依賴的問題。

5.1 不支持單例Bean的setter循環依賴的特殊情況

運行4.1節中SpecialCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" java.lang.StackOverflowError

可以看到,實際拋出的是StackOverflowError錯誤。這個錯誤信息本質上不是Spring拋出的,而是JVM拋出的。根本原因其實是出在SpecialCircularBeanA和SpecialCircularBeanB兩個類的toString()方法上。

當在SpecialCircularTest類的main()方法中打印specialCircularBeanA時,默認會調用SpecialCircularBeanA類的toString()方法,在SpecialCircularBeanA類的toString()方法中,會拼接specialCircularBeanB對象,此時又會調用SpecialCircularBeanB類的toString()方法。而在SpecialCircularBeanB類的toString()方法中,又會拼接specialCircularBeanA對象,此時又會調用SpecialCircularBeanA類的toString()方法。在SpecialCircularBeanA類的toString()方法中,又會拼接specialCircularBeanB對象,繼而調用SpecialCircularBeanB類的toString()方法....如此反復,造成了死循環。

簡單點說,就是在SpecialCircularBeanA類的toString()方法中調用了SpecialCircularBeanB類的toString()方法,在SpecialCircularBeanB類的toString()方法中調用了SpecialCircularBeanA類的toString()方法,造成了死循環,最終拋出StackOverflowError錯誤。

5.2 不支持多例Bean的setter循環依賴

運行4.2節中PrototypeCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'prototypeCircularBeanA': Unsatisfied dependency expressed through field 'prototypeCircularBeanB': Error creating bean with name 'prototypeCircularBeanB': Unsatisfied dependency expressed through field 'prototypeCircularBeanA': Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/***************省略其他輸出信息**************/
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'prototypeCircularBeanB': Unsatisfied dependency expressed through field 'prototypeCircularBeanA': Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/***************省略其他輸出信息**************/
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'prototypeCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/***************省略其他輸出信息**************/

Spring拋出了循環依賴的異常,說明Spring不支持多例Bean的setter循環依賴。接下來就分析下Spring中為啥不支持多例Bean的setter循環依賴。具體分析步驟如下所示。

(1)解析AbstractBeanFactory類的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法

源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)。先來看在doGetBean()方法中創建多例Bean對象的邏輯,如下所示。

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        /**********省略其他代碼*************/
    }
    else {
        /**********省略其他代碼*************/
        try {
            /**********省略其他代碼*************/
            else if (mbd.isPrototype()) {
                Object prototypeInstance = null;
                try {
                    beforePrototypeCreation(beanName);
                    prototypeInstance = createBean(beanName, mbd, args);
                }
                finally {
                    afterPrototypeCreation(beanName);
                }
                beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            }
  /**********省略其他代碼*************/
        }
        catch (BeansException ex) {
            /**********省略其他代碼*************/
        }
        finally {
           /**********省略其他代碼*************/
        }
    }
    return adaptBeanInstance(name, beanInstance, requiredType);
}

可以看到,在多例Bean模式下,創建Bean對象之前會調用beforePrototypeCreation()方法,在創建Bean對象之后會調用afterPrototypeCreation()方法。

(2)解析AbstractBeanFactory類的beforePrototypeCreation(String beanName)方法

源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#beforePrototypeCreation(String beanName)。

protected void beforePrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal == null) {
        this.prototypesCurrentlyInCreation.set(beanName);
    }
    else if (curVal instanceof String strValue) {
        Set<String> beanNameSet = new HashSet<>(2);
        beanNameSet.add(strValue);
        beanNameSet.add(beanName);
        this.prototypesCurrentlyInCreation.set(beanNameSet);
    }
    else {
        Set<String> beanNameSet = (Set<String>) curVal;
        beanNameSet.add(beanName);
    }
}

可以看到,Spring在創建多例Bean時,會在beforePrototypeCreation()方法中,使用prototypesCurrentlyInCreation記錄正在創建中的Bean,那prototypesCurrentlyInCreation又是個什么鬼呢?

prototypesCurrentlyInCreation的源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#prototypesCurrentlyInCreation,如下所示。

private final ThreadLocal<Object> prototypesCurrentlyInCreation = new NamedThreadLocal<>("Prototype beans currently in creation");

也就是說,Spring在創建多例Bean時,會使用一個ThreadLocal類型的變量prototypesCurrentlyInCreation來記錄當前線程正在創建中的Bean。并且根據beforePrototypeCreation()方法的源碼又可以看出,在prototypesCurrentlyInCreation變量中使用一個Set集合來存儲正在創建中的Bean。由于Set集合不存在重復對象,所以這樣就能夠保證在一個線程中只能有一個相同的Bean正在被創建。

(3)解析AbstractBeanFactory類的afterPrototypeCreation(String beanName)方法

源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#afterPrototypeCreation(String beanName)。

protected void afterPrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal instanceof String) {
        this.prototypesCurrentlyInCreation.remove();
    }
    else if (curVal instanceof Set<?> beanNameSet) {
        beanNameSet.remove(beanName);
        if (beanNameSet.isEmpty()) {
            this.prototypesCurrentlyInCreation.remove();
        }
    }
}

可以看到,afterPrototypeCreation()方法中主要是對prototypesCurrentlyInCreation中存儲的Bean進行移除操作。

綜合beforePrototypeCreation()方法和afterPrototypeCreation()方法可以看出,Spring在創建多例Bean之前,會將當前線程正在創建的Bean存入prototypesCurrentlyInCreation中,待Bean對象實例化完成后,就從prototypesCurrentlyInCreation中移除正在創建的Bean。

(4)返回AbstractBeanFactory類的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法。此時重點關注doGetBean()方法開始部分的代碼片段。

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)throws BeansException {
    String beanName = transformedBeanName(name);
    Object beanInstance;
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
         /**********省略其他代碼*************/
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    else {
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    /**********省略其他代碼*************/
    }
    return adaptBeanInstance(name, beanInstance, requiredType);
}

在AbstractBeanFactory類的doGetBean()方法開始的部分,會調用getSingleton()方法從緩存中獲取單例Bean對象,首先會執行if條件進行判斷,如果獲取到的單例Bean對象為空,說明此時可能是第一次執行doGetBean()方法,也可能是創建的多例Bean。接下來會進入else分支邏輯,在else分支邏輯中,首先會判斷當前線程是否已經存在正在創建的Bean,如果存在,則直接拋出BeanCurrentlyInCreationException異常。

(5)解析AbstractBeanFactory類的isPrototypeCurrentlyInCreation(String beanName)方法

源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#isPrototypeCurrentlyInCreation(String beanName)。

protected boolean isPrototypeCurrentlyInCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set<?> set && set.contains(beanName))));
}

所以,在Spring創建多例Bean時,無法解決Bean的循環依賴。如果在創建多例Bean的過程中,發現存在循環依賴,則直接拋出BeanCurrentlyInCreationException異常。

5.3 不支持代理對象的setter循環依賴

運行4.3節中ProxyCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'proxyCircularBeanA': Bean with name 'proxyCircularBeanA' has been injected into other beans [proxyCircularBeanB] 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.

從輸出的結果信息可以看出,Spring拋出了循環依賴的異常。接下來,具體分析Spring為何不支持代理對象的setter循環依賴。

(1)解析AbstractAutowireCapableBeanFactory類的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法。

通過前面章節對于創建Bean的流程分析可知,無論是創建單例Bean還是創建多例Bean,Spring都會執行到AbstractAutowireCapableBeanFactory類的doCreateBean()方法中。

源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。重點關注如下代碼片段。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
 /********省略其他代碼**********/ 
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                                      isSingletonCurrentlyInCreation(beanName));
    /********省略其他代碼**********/
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + 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;
}

可以看到,在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中,會判斷從二級緩存中獲取到的對象是否等于原始對象,代碼片段如下所示。

Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
    if (exposedObject == bean) {
        exposedObject = earlySingletonReference;
    }

此時,如果是代理對象的話,二級緩存中會存放由AOP生成出來的代理對象,與原始對象不相等。所以,會拋出BeanCurrentlyInCreationException異常。

另外,仔細觀察AbstractAutowireCapableBeanFactory類的doCreateBean()方法時,會發現如果從二級緩存中獲取到的earlySingletonReference對象為空,就會直接返回,不會拋出BeanCurrentlyInCreationException異常。由于Spring默認會按照文件全路徑遞歸搜索,并且會按照路徑+文件名的方式進行排序,排序靠前的Bean先被加載。所以,將標注了@Async注解的類排在后面即可解決循環依賴的問題。

5.4 不支持構造方法的循環依賴

運行4.4節中ConstructCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'constructCircularBeanA' defined in file [D:\Workspaces\2022\spring\spring-annotation-book\spring-annotation\spring-annotation-chapter-20\target\classes\io\binghe\spring\annotation\chapter20\construct\bean\ConstructCircularBeanA.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanB' defined in file [D:\Workspaces\2022\spring\spring-annotation-book\spring-annotation\spring-annotation-chapter-20\target\classes\io\binghe\spring\annotation\chapter20\construct\bean\ConstructCircularBeanB.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/*************省略其他信息**************/
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'constructCircularBeanB' defined in file [D:\Workspaces\2022\spring\spring-annotation-book\spring-annotation\spring-annotation-chapter-20\target\classes\io\binghe\spring\annotation\chapter20\construct\bean\ConstructCircularBeanB.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?
/*************省略其他信息**************/
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'constructCircularBeanA': Requested bean is currently in creation: Is there an unresolvable circular reference?

可以看到,Spring拋出了循環依賴的異常,說明Spring不支持基于構造方法的循環依賴。接下來,具體分析下Spring不支持基于構造方法的循環依賴的原因。具體分析步驟如下所示。

(1)解析AbstractBeanFactory類的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法

源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)。

此時重點關注如下代碼片段。

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    String beanName = transformedBeanName(name);
    Object beanInstance;
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
       /********省略其他代碼**********/
    } else {
       /********省略其他代碼**********/
            if (mbd.isSingleton()) {
                sharedInstance = getSingleton(beanName, () -> {
                    try {
                        return createBean(beanName, mbd, args);
                    }
                    catch (BeansException ex) {
                        /********省略其他代碼**********/
                    }
                });
                beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            }
   /********省略其他代碼**********/
        }
        catch (BeansException ex) {
           /********省略其他代碼**********/
        }
        finally {
            /********省略其他代碼**********/
        }
    }
    return adaptBeanInstance(name, beanInstance, requiredType);
}

可以看到,在AbstractBeanFactory類的doGetBean()方法中,會調用getSingleton()方法。

(2)解析DefaultSingletonBeanRegistry類的getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法

源碼詳見:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory)。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                afterSingletonCreation(beanName);
            }
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

可以看到,在getSingleton()方法創建Bean之前會調用beforeSingletonCreation()方法,在創建Bean之后會調用afterSingletonCreation()方法。

(3)解析DefaultSingletonBeanRegistry類的beforeSingletonCreation(String beanName)方法

源碼詳見:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#beforeSingletonCreation(String beanName)。

protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}

可以看到,在創建單例Bean之前,會將要創建的Bean的名稱添加到singletonsCurrentlyInCreation中,添加失敗,說明singletonsCurrentlyInCreation集合中已經存在當前Bean的名稱,發生了循環依賴,就會拋出BeanCurrentlyInCreationException異常。那么singletonsCurrentlyInCreation又是個什么鬼呢?

singletonsCurrentlyInCreation的源碼詳見:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#singletonsCurrentlyInCreation。

private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

可以看到,singletonsCurrentlyInCreation其實就是一個Set集合。也就是說,Spring會在創建單例Bean之前,將正在創建的Bean的名稱添加到singletonsCurrentlyInCreation集合中。

(4)解析DefaultSingletonBeanRegistry類的afterSingletonCreation(String beanName)方法

源碼詳見:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#afterSingletonCreation(String beanName)。

protected void afterSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
        throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
    }
}

可以看到,在afterSingletonCreation()方法中,主要是移除singletonsCurrentlyInCreation中對應的Bean的名稱。

綜合beforeSingletonCreation()和afterSingletonCreation()兩個方法可以看出,Spring在創建單例Bean之前,會將Bean的名稱存入singletonsCurrentlyInCreation集合中,實例化Bean之后,會將Bean的名稱從singletonsCurrentlyInCreation集合中移除。并且,Spring在創建單例Bean之前,會調用beforeSingletonCreation()方法將要創建的Bean的名稱添加到singletonsCurrentlyInCreation中,添加失敗,說明singletonsCurrentlyInCreation集合中已經存在當前Bean的名稱,發生了循環依賴,就會拋出BeanCurrentlyInCreationException異常。

5.5 不支持@DependsOn的循環依賴

運行4.5節中DependsOnCircularTest類的main()方法,輸出的結果信息如下所示。

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dependsOnCircularBeanB' defined in file [D:\Workspaces\2022\spring\spring-annotation-book\spring-annotation\spring-annotation-chapter-20\target\classes\io\binghe\spring\annotation\chapter20\dependson\bean\DependsOnCircularBeanB.class]: Circular depends-on relationship between 'dependsOnCircularBeanB' and 'dependsOnCircularBeanA'

從輸出的結果信息可以看出,Spring拋出了循環依賴的異常。說明Spring不支持@DependsOn注解的循環依賴。接下來,就分析下Spring不支持@DependsOn注解的循環依賴的原因。具體分析步驟如下所示。

解析AbstractBeanFactory類的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法

源碼詳見:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)。重點關注如下代碼片段。

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    String beanName = transformedBeanName(name);
    Object beanInstance;
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
         /*********省略其他代碼*********/
    }
    else {
        /*********省略其他代碼*********/
        try {
            /*********省略其他代碼*********/
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,  "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    registerDependentBean(dep, beanName);
                    try {
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,  "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }
             /*********省略其他代碼*********/
        }
        catch (BeansException ex) {
            /*********省略其他代碼*********/
        }
        finally {
           /*********省略其他代碼*********/

        }
    }
    return adaptBeanInstance(name, beanInstance, requiredType);
}

可以看到,在AbstractBeanFactory類的doGetBean(String name, @Nullable ClassrequiredType, @Nullable Object[] args, boolean typeCheckOnly)方法中,會判斷是否存在@DependsOn注解的循環依賴,如果存在則拋出BeanCreationException異常。所以,Spring不支持@DependsOn注解的循環依賴。

5.6 支持單例Bean的setter循環依賴

運行4.6節中SingletonCircularTest類的main()方法,輸出的結果信息如下所示。

singletnotallow===>>>io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanA@41e1e210
singletnotallow===>>>io.binghe.spring.annotation.chapter20.singleton.bean.SingletonCircularBeanB@be35cd9

可以看到,正確輸出了SingletonCircularBeanA類型的Bean對象和SingletonCircularBeanB類型的Bean對象。說明Spring支持基于單例Bean的setter方法的循環依賴。接下來,就分析下Spring為何支持單例Bean的setter循環依賴。

(1)三級緩存

Spring使用了三級緩存來解決循環依賴的問題,三級緩存的源碼詳見:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry。

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

其中,每個Map的含義如下所示。

  • singletonObjects:一級緩存,存儲所有實例化,并且為屬性賦值的單實例Bean。
  • earlySingletonObjects:二級緩存,存儲實例化后還沒來得及為屬性賦值的單實例Bean。
  • singletonFactories:三級緩存,存儲生產單實例Bean的工廠。

關于三級緩存,面試中經常會問如下兩個問題,這里也給大家分享下。

  • Spring解決循環依賴為什么需要二級緩存?

二級緩存主要是為了分離創建出的完整的Bean和未對屬性賦值的Bean,二級緩存中實際主要存儲的是未對屬性賦值的Bean,這樣做的目的就是為了防止在多線程并發的場景中,讀取到還未創建完成的Bean。所以,為了保證在多線程并發的環境中,讀取到的Bean是完整的(已經為屬性賦值),不會讀取到未對屬性賦值的Bean,需要使用二級緩存解決循環依賴。

另外,就一、二、三級緩存而言,二級緩存主要存儲的是三級緩存創建出來的,并且未對屬性賦值的Bean,這樣做的目的也是為了防止三級緩存中的工廠類重復執行創建對象的邏輯。

  • Spring只用二級緩存能否解決循環依賴?為什么一定要用三級緩存來解決循環依賴呢?

其實,Spring使用二級緩存就完全能夠解決循環依賴的問題,也可以支持Spring基于BeanPostProcessor的擴展能力。但是,由于Spring中的方法在設計上遵循了單一職責的原則,一個方法通常只做一件事情,getBean()方法就是獲取Bean對象。但是,調用BeanPostProcessor創建動態代理是處于創建Bean的過程,如果在getBean()中實現這個邏輯,顯然代碼邏輯比較耦合。為了解決代碼耦合的問題,保持方法的職責單一,方面后期維護。需要將創建動態代理的BeanPostProcessor放在創建Bean的方法中。并且將判斷是否存在循環依賴的邏輯放在getSingleton()方法中。此時就需要三級緩存,在三級緩存中存放一個工廠接口,在接口的實現類中調用BeanPostProcessor創建動態代理對象。為了防止重復創建代理對象,將三級緩存中創建的代理對象存入二級緩存。在Spring中使用三級緩存完美解決了解耦、性能、擴展的問題。

(2)創建單例工廠

Spring在創建Bean對象時,會先創建一個和Bean的名稱相同的單例工廠,并將Bean先放入單例工廠中。

源碼詳見:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
 /**********省略其他代碼***********/
    if (earlySingletonExposure) {
       /**********省略其他代碼***********/
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
 /**********省略其他代碼***********/
    return exposedObject;
}

在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中調用了addSingletonFactory()方法。

addSingletonFactory()方法的源碼詳見:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)。

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

可以看到,addSingletonFactory()方法的作用是將正在創建中的Bean的單例工廠,存放在三級緩存里,這樣就保證了在循環依賴查找的時候是可以找到Bean的引用的。

(3)讀取緩存數據

具體讀取緩存獲取Bean的過程在類DefaultSingletonBeanRegistry的getSingleton()方法中,源碼詳見:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference)。

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

通過上面的源碼我們可以看到,在獲取單例Bean的時候,會先從一級緩存singletonObjects里獲取,如果沒有獲取到(說明不存在或沒有實例化完成),會去第二級緩存earlySingletonObjects中去找,如果還是沒有找到的話,就會三級緩存中獲取單例工廠singletonFactory,通過從singletonFactory中獲取正在創建中的引用,將singletonFactory存儲在earlySingletonObjects 二級緩存中,這樣就將創建中的單例引用從三級緩存中升級到了二級緩存中,二級緩存earlySingletonObjects,是會提前暴露已完成構造,還未執行屬性注入的單例bean的。這個時候如何還有其他的bean也是需要屬性注入,那么就可以直接從earlySingletonObjects中獲取了。

注意:為了防止多線程并發環境下重復執行三級緩存中創建Bean的過程,在對singletonObjects加鎖后,還會先從一級緩存singletonObjects中獲取數據,如果數據不存在則從二級緩存earlySingletonObjects中獲取數據,如果數據仍然不存在,才會從三級緩存singletonFactories中獲取singletonFactory,調用singletonFactory的getObject()方法獲取實例化但未對屬性賦值的Bean對象,將其存入二級緩存,并且從三級緩存中移除對應的singletonFactory。

(4)解決循環依賴的完整流程圖

最后給出Spring支持單例Bean的setter循環依賴的完整流程圖,如圖20-6所示。

圖片

大家可以按照20-6的邏輯分析Spring解決循環依賴的代碼,就相對比較清晰了。這里,就不再分析具體源碼了。

六、總結

Spring的循環依賴問題介紹完了,我們一起總結下吧!

本章,主要詳細分析了Spring的循環依賴問題,首先介紹了緩存依賴的基本概念和循環依賴的類型。隨后以案例的形式詳細介紹了循環依賴的場景,并詳細分析了Spring循環依賴的底層解決方案。通過分析得知:Spring默認會支持單例Bean的setter循環依賴,對于其他情況下的循環依賴,Spring默認是不支持的。并且,最后給出了Spring解決循環依賴的流程圖。

七、思考

既然學完了,就開始思考幾個問題吧?

關于Spring的循環依賴,通常會有如下幾個經典面試題:

  • 什么是循環依賴問題?
  • 循環依賴有哪些類型?
  • 在Spring中支持哪種循環依賴?
  • 列舉幾種Spring不支持的循環依賴的場景,為什么不支持?
  • Spring解決循環依賴的流程是什么?
  • Spring解決緩存依賴時,二級緩存的作用是什么?
  • Spring只用二級緩存能否解決循環依賴?為什么一定要用三級緩存來解決循環依賴呢?
  • 你從Spring解決循環依賴的設計中得到了哪些啟發?
責任編輯:武曉燕 來源: 冰河技術
相關推薦

2021-10-18 11:58:56

負載均衡虛擬機

2022-09-06 08:02:40

死鎖順序鎖輪詢鎖

2022-09-14 09:01:55

shell可視化

2022-07-19 16:03:14

KubernetesLinux

2020-07-09 07:54:35

ThreadPoolE線程池

2021-01-19 05:49:44

DNS協議

2021-02-26 05:17:38

計算機網絡二進制

2020-07-15 08:57:40

HTTPSTCP協議

2020-11-16 10:47:14

FreeRTOS應用嵌入式

2024-03-07 18:11:39

Golang采集鏈接

2022-10-10 08:35:17

kafka工作機制消息發送

2021-01-04 05:53:35

MyBatis底層Java

2023-06-12 08:49:12

RocketMQ消費邏輯

2024-05-10 12:59:58

PyTorch人工智能

2024-01-11 09:53:31

面試C++

2022-09-08 10:14:29

人臉識別算法

2024-01-05 08:30:26

自動駕駛算法

2021-08-26 05:02:50

分布式設計

2022-07-15 16:31:49

Postman測試

2021-06-04 07:27:24

sourcemap前端技術
點贊
收藏

51CTO技術棧公眾號

一区二区三区久久| 久久夜色精品| 亚洲国产精彩中文乱码av| 毛片在线播放视频| 黄网站在线观看| 久久99在线观看| 国自产精品手机在线观看视频| 魔女鞋交玉足榨精调教| 天天综合91| 午夜欧美一区二区三区在线播放| 日韩hmxxxx| 亚洲精品视频网| 日本午夜精品一区二区三区电影 | 国产精品免费网站| 欧美黄色免费观看| 精品一区二区三区中文字幕老牛| 日韩一区二区三区三四区视频在线观看| 黄页网站大全在线观看| 婷婷在线视频| 91日韩一区二区三区| 成人福利免费观看| 日韩黄色片网站| 国色天香一区二区| 色妞色视频一区二区三区四区| 国产视频久久久久久| 91精品福利观看| 色视频一区二区| 蜜桃传媒一区二区三区| 1stkiss在线漫画| 中文字幕av不卡| 欧美激情导航| 少妇又色又爽又黄的视频| 国产一区二区三区四区在线观看| 国产福利精品av综合导导航| 国产无遮挡又黄又爽在线观看| 色婷婷亚洲mv天堂mv在影片| 亚洲男人天堂视频| 久久久久久婷婷| 老司机亚洲精品一区二区| 欧美少妇xxx| 蜜臀av午夜一区二区三区| av资源在线| 亚洲一二三区不卡| 免费看污污视频| 免费在线你懂的| 中文字幕精品三区| 日本一区不卡| 国产日本在线| 久久精品男人天堂av| 久久99影院| 婷婷综合激情网| 福利91精品一区二区三区| 成人免费视频a| 国产露脸91国语对白| 麻豆一区二区三| 91精品免费看| 国产美女精品视频国产| 精品在线播放午夜| 91最新在线免费观看| 国产内射老熟女aaaa∵| 国精产品一区一区三区mba桃花| 亚洲男人都懂第一日本| 日本激情一区二区| 一区二区亚洲精品| 欧美激情免费看| 青青草国产在线观看| 亚洲精品在线观看91| 久久久99久久精品女同性| 999精品视频在线观看播放| 亚洲a在线视频| 久久福利网址导航| 黄色小说在线观看视频| 亚洲精品社区| 国产97色在线|日韩| 日韩黄色片网站| 久久精品99国产精品日本| 91在线观看免费网站| 丰满少妇被猛烈进入| 成+人+亚洲+综合天堂| 农村寡妇一区二区三区| 阿v免费在线观看| 成人免费在线视频| 无码熟妇人妻av在线电影| 少妇视频在线观看| 欧美日韩一卡二卡三卡| 欧美性猛交xx| 亚洲bt欧美bt精品777| 一区二区三区动漫| 顶臀精品视频www| 国产欧美日韩综合一区在线播放| 国产精品91久久| 国产婷婷在线视频| 91小视频在线观看| 亚洲视频在线二区| 蜜桃麻豆影像在线观看| 欧美日韩一本到| 蜜臀av粉嫩av懂色av| 欧美理论电影大全| 欧美激情精品在线| 成人黄色片在线观看| 国产·精品毛片| 日韩欧美亚洲精品| 国精一区二区三区| 欧美三级日本三级少妇99| 日韩女优在线视频| 成人3d精品动漫精品一二三| 国语对白做受69| 国产美女精品视频国产| 国产天堂亚洲国产碰碰| 免费看欧美一级片| 成人日韩av| 日韩高清av在线| 内射一区二区三区| 久久久久国产一区二区| www.成人av.com| 四虎久久免费| 色成人在线视频| 岛国av免费观看| 国产精品国产一区| 国产成人精品久久亚洲高清不卡| 成人av无码一区二区三区| 欧美国产日本视频| 久久无码高潮喷水| 91在线一区| 久青草国产97香蕉在线视频| 91porny九色| 91在线观看下载| 久久99久久久久久| 欧美中文高清| 久久中国妇女中文字幕| 中文字幕第315页| 久久久久88色偷偷免费| 亚洲精品蜜桃久久久久久| 国产精选久久| 久久国产精品亚洲| 91尤物国产福利在线观看| 欧美激情一区二区三区不卡| 国产最新免费视频| 欧美1区2区3区4区| 91高清免费视频| 午夜成人免费影院| 97成人超碰| 国产欧美日韩视频在线| 日韩精品视频在线观看免费| 欧美日韩中文字幕在线观看| 国内精品免费**视频| 一区二区三区我不卡| 成人午夜亚洲| 中文字幕久久精品| 中文字幕在线播放av| 国产女主播视频一区二区| 久久久久久久少妇| 成人久久综合| 国产精品欧美久久久| 91精品国产91久久久久游泳池| 日本大香伊一区二区三区| 在哪里可以看毛片| 日韩av午夜在线观看| 亚洲成人在线视频网站| 看片一区二区| 欧美精品一区二区免费| 亚洲黄色在线观看视频| 亚洲国产精品一区二区久久| xxxwww国产| 国产一区二区三区的电影| 欧美1o一11sex性hdhd| av免费在线一区| 日韩少妇与小伙激情| 国产三级自拍视频| 亚洲午夜激情av| 日本一区二区三区网站| 日韩成人一级大片| 天堂av免费看| 红杏一区二区三区| 日韩美女中文字幕| 日本激情在线观看| 精品黑人一区二区三区久久| 中国一级特黄毛片| 亚洲国产精品av| 日本亚洲一区二区三区| 亚洲精品社区| 亚洲国产午夜伦理片大全在线观看网站| 欧美成人一二区| 久久亚洲国产精品| 色婷婷av一区二区三区之e本道| 欧美视频一区二区三区…| 亚洲图片第一页| 大胆亚洲人体视频| 亚洲最大综合网| 欧美日韩亚洲三区| 日本在线观看一区| 欧美黄视频在线观看| 欧美在线观看网站| 快射av在线播放一区| 日韩av网站导航| 一级特黄色大片| 精品久久久久久久久中文字幕| 国产精品无码无卡无需播放器| 国产成人免费网站| 少妇激情一区二区三区| 激情综合视频| 一卡二卡3卡四卡高清精品视频| 91精品入口| 国产美女精彩久久| 免费在线小视频| 免费99精品国产自在在线| 瑟瑟在线观看| 日韩欧美的一区| 五月天中文字幕| 五月激情六月综合| 五月天丁香激情| 亚洲国产精品二十页| 欧产日产国产精品98| 国产专区欧美精品| 久久精品免费网站| 夜夜嗨一区二区三区| 日韩中文在线字幕| 久久在线免费| 欧美一区二区三区精美影视| 成人性生交大片免费看96| 国产精品揄拍一区二区| 亚洲天堂手机| 久久免费精品视频| 日本中文字幕中出在线| 久久久国产一区二区| av二区在线| 亚洲视频一区二区| 欧美美女色图| 亚洲精品电影网站| 亚洲精品成人区在线观看| 欧美精选在线播放| 亚洲视频一区在线播放| 91国产精品成人| 69国产精品视频免费观看| 午夜成人在线视频| 国产中文字字幕乱码无限| 一区二区三区四区中文字幕| 国产麻豆视频在线观看| 中文一区一区三区高中清不卡| 中文字幕国产综合| 久久久精品天堂| 手机免费看av| 国产午夜精品久久| 日本黄色小视频在线观看| 国产亚洲综合色| 干b视频在线观看| 国产午夜精品一区二区三区四区| 成年人网站免费看| 久久精品在线观看| 色哟哟精品观看| 亚洲国产激情av| 精品视频第一页| 亚洲三级在线看| 久久视频免费看| 午夜视频一区在线观看| 久久免费激情视频| 色综合色综合色综合色综合色综合 | 在线电影欧美日韩一区二区私密| 加勒比一区二区三区在线| 亚洲一区二区久久久| a黄色在线观看| 久久精品一区中文字幕| 2020国产在线视频| 久久久噜噜噜久久| 亚洲欧美一区二区三区| 国产精品国产三级国产专播精品人| 国产经典一区| 亚洲影院污污.| 7777精品| 欧美日韩综合久久| 欧洲福利电影| av电影一区二区三区| 精品福利电影| 日本激情视频在线| 国产传媒一区在线| 97人妻精品一区二区三区免| 国产欧美综合色| 唐朝av高清盛宴| 亚洲超碰97人人做人人爱| 无码aⅴ精品一区二区三区| 欧美日韩久久一区| 欧洲成人一区二区三区| 亚洲一区二区久久| 污视频网站免费在线观看| 91av在线免费观看| 日韩城人网站| 国产精品一区二区av| 欧美综合视频| 丁香婷婷综合激情| 日本女优在线视频一区二区| 超碰人人cao| 久久久久久久久久美女| 青青草手机在线视频| 91成人国产精品| 亚洲精品成人电影| 中文字幕欧美精品日韩中文字幕| 欧美卡一卡二| 国产噜噜噜噜久久久久久久久| aaa国产精品视频| 亚洲精品中文字幕乱码三区不卡| 亚洲国产电影| 日韩中文字幕a| www日韩大片| 久久精品性爱视频| 欧美日韩精品一区二区三区蜜桃| 可以免费观看的毛片| 色偷偷av一区二区三区乱| 欧美a级在线观看| 91精品国自产在线观看| 日韩综合网站| 最近免费中文字幕中文高清百度| 国产精品亚洲综合一区在线观看| 蜜桃传媒一区二区亚洲| 午夜精品久久久久久久蜜桃app| 一区二区美女视频| 国产一区二区三区欧美| 1区2区在线| 成人自拍偷拍| 雨宫琴音一区二区三区| 狠狠躁狠狠躁视频专区| 国产夜色精品一区二区av| 国产精品xxxx喷水欧美| 91精品国产福利| 成人在线播放视频| 青草成人免费视频| 欧美久久香蕉| 欧美黑人经典片免费观看| 丁香啪啪综合成人亚洲小说| 欧洲美女女同性互添| 欧美日韩国产成人在线91| 黄色大片在线看| 日本一区二区三区在线播放| 欧美大片网址| 青青草国产精品视频| 不卡视频一二三四| 动漫精品一区一码二码三码四码| 91精品国产91久久综合桃花| 黄在线免费看| 91精品国产自产在线观看永久| 成人写真视频| 杨幂毛片午夜性生毛片| 国产日韩av一区二区| 波多野结衣小视频| 国产一区av在线| 成人福利片在线| 亚洲春色在线| 麻豆久久一区二区| 多男操一女视频| 欧美一区二区三区视频在线| 宅男在线观看免费高清网站| 99影视tv| 在线不卡视频| 亚洲av片不卡无码久久| 色综合天天综合给合国产| 蜜芽tv福利在线视频| 国产精品91久久久| 99久精品视频在线观看视频| 欧美性受xxxx黒人xyx性爽| 一区二区三区四区乱视频| 成人免费观看在线视频| 97色在线观看| 国产亚洲电影| 97超碰成人在线| 一区二区三区中文字幕电影| 色wwwwww| 国产精品久久久久久av| 99久久www免费| 亚洲精品鲁一鲁一区二区三区| 亚洲国产日韩一级| 免费一级在线观看播放网址| 国产精品亚洲欧美导航| 天天综合国产| 午夜男人的天堂| 欧亚一区二区三区| bestiality新另类大全| 好看的日韩精品视频在线| 日本免费新一区视频| 日韩黄色免费观看| 亚洲精品久久久久中文字幕二区 | 亚洲一级影院| 大又大又粗又硬又爽少妇毛片 | 狠狠色狠狠色综合婷婷tag| 久久人人爽av| 亚洲va欧美va人人爽| 国产在线资源| 99久久综合狠狠综合久久止| 亚洲欧美日韩国产综合精品二区| 18精品爽国产三级网站| 欧美va亚洲va在线观看蝴蝶网| 性爽视频在线| 欧美日韩一级在线| 国产亚洲1区2区3区| 国产三级视频在线播放| 日本久久中文字幕| 欧美.www| 夫妇露脸对白88av| 亚洲国产成人精品久久久国产成人一区| **欧美日韩在线观看| 久久人妻无码一区二区| 欧美国产日本视频|