• Spring中Bean循环依赖详解


    1. Bean创建的主要流程

    先获取,没有的话再创建,如下图所示

     创建bean的主要流程如下:

    • getSingleton:从容器里面获取单例的bean,没有的话创建
    • doCreateBean:没有就创建bean
    • addSIngletonFactory:将创建bean的 factory 缓存起来
    • populateBean:创建完了以后,要填充属性
    • addSingleton:填充完了以后,再添加到容器进行使用

    依赖是在目标对象里,代理对象的字段没有值,如下图所示:

    2. 解决循环依赖时需要的缓存

    Spring在解决循环依赖时使用了 三级缓存,如果没有代理的话,用一级缓存就可以解决。二三级缓存主要是为了解决代理的生成。

    一级缓存(也叫单例池): singletonObjects,存放已经经历了完整生命周期的Bean对象。

    二级缓存: earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完整),存放半成品的Bean。

    三级缓存: singletonFactories,存放可以生成Bean的工厂,主要是解决aop特性,()->getEarlyBeanReference(beanName,mbd,bean),getEarlyBeanReference()返回增强后的bean。

    3. 循环依赖具体流程分析

    在代码中创建3个Bean,然后相互依赖,再创建切面,让这几个Bean生成代理。

    3.1 代码示例

    1. @Component
    2. public class TestA {
    3. @Autowired
    4. TestB testB;
    5. public void test(){
    6. System.out.println("----aaa----");
    7. }
    8. }
    9. @Component
    10. public class TestB {
    11. @Autowired
    12. TestC testC;
    13. public void test(){
    14. System.out.println("----bbb----");
    15. }
    16. }
    17. @Component
    18. public class TestC {
    19. @Autowired
    20. TestA testA;
    21. public void test(){
    22. System.out.println("----ccc----");
    23. }
    24. }
    25. @Aspect
    26. @Configuration
    27. public class TestAspect {
    28. @Pointcut("execution(* com.shopping.demo24.component.*.test(..))")
    29. public void pointcut() {}
    30. @Around("pointcut()")
    31. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    32. System.out.println(this.getClass().getSimpleName()+"-around:before");
    33. Object result=joinPoint.proceed();
    34. System.out.println(this.getClass().getSimpleName()+"-around:after");
    35. return result;
    36. }
    37. }

    3.2 调试分析

    1. 获取a->创建a->将a添加到singletonFactories->设置依赖b->获取b

    2. 创建b->将b添加到singletonFactories->设置依赖c开始->获取c

    3.创建c->将c添加到singletonFactories

    此时缓存池中的 bean 如下,没有写的为空

    singletonFactories:a, b, c

     4. 给c设置依赖a开始->获取a

    先从singletonObjects中获取,没有,再从earlySingletonObjects中获取,也没有,最后在singletonFactories的ObjectFactory中获取。获取以后添加到earlySingletonObjects中,这里面的a是增强后的a,但是依赖的属性没有设置。再从singletonFactories中删除

    此时

    earlySingletonObjects: a

    singletonFactories: b,c

     ObjectFactory的值为 () -> getEarlyBeanReference(beanName, mbd, bean)

     从ObjectFactory获取时会对a调用BeanPostProcessor的getEarlyBeanReference()返回增强后的a

     

     包装对象,生成代理

     a 在这里增强后,在后面的postProcessAfterInitialization()方法中,创建代理对象(增强对象)的AbstractAutoProxyCreator中如果有对应的earlyProxyReferences记录(这里的记录不是增强对象,而是原始对象),就不会再被增强。

     

     5. 通过反射,给c设置依赖

    给c设置依赖a结束,调用initializeBean()方法,生成代理对象,创建完c,将c添加到singletonObjects中,删除earlySingletonObjects和singletonFactories中的对应值。

     此时

    earlySingletonObjects:a

    singletonFactories: b

    singletonObjects: c

    6: 给b设置依赖c结束,调用initializeBean()方法,生成代理对象, 创建完b,将b添加到singletonObjects中b是代理对象

    earlySingletonObjects:a

    singletonFactories: null

    singletonObjects: b,c

    7: 给a设置依赖b完成

    此时,对于 a, 调用initializeBean()方法返回的不是增强对象,

     

    因为earlyProxyReferences中存在a,表示它已经被增强过

     获取earlySingletonObjects中的a,这里的a是增强对象。

    创建完a,将a添加到singletonObjects中

    earlySingletonObjects:null

    singletonFactories: null

    singletonObjects: b,c,a

    至此,a,b,c 三个bean创建完成,也都加到了 singletonObjects 中,其他两个缓存池都为空。

  • 相关阅读:
    Cookies
    Docker安装ElasticSearch 版本7.6.2
    线性代数的本质(二)——线性变换与矩阵
    改变alert弹出框页面
    Yolov8部署——vs2019遇到的问题
    Qt 为Android app添加系统签名
    OpenGL 着色器使用
    leetcode 每日一题复盘(11.20~11.26)
    linux上lua版本的替换和luasocket的安装和使用
    【基于element ui的color选择器】基于element ui的color选择器
  • 原文地址:https://blog.csdn.net/CaptHua/article/details/126527859