• spring 如何检测到循环依赖/如何解决循环依赖


    spring针对循环依赖问题 不能完全解决 对于不能解决的只能检测到并抛出异常

    1. spring针对构造器方法的 单实例对象和原型对象是无法解决循环依赖问题的

         先说结论,

      针对单例对象 getSingleton方法中 有个beforeSingletonCreation 方法 这个方法是用来检测循环依赖的

      原型对象 isPrototypeCurrentlyInCreation方法beforePrototypeCreation方法配合检测循环依赖

      注: inCreationCheckExclusions和singletonsCurrentlyInCreation 是两个set

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

      以AB两个单例对象举例,

      

    1 Class A(){
    2     A(B b){
    3   }  
    4 }
    5 
    6 Class B(){
    7     B(A a){
    8   }  
    9 }

      对象A在实例化的时候(getBean方法),会先执行beforeSingletonCreation方法 吧自己的beanName放入set中,然后去执行实例化 解析构造器方法的时候发现 需要用到对象B ,所以去getBean(B) 

       B对象在一级缓存是没有的(因为是还未实例化),所以去创建单例B,会执行和A一样的操作,把自己的beanName放入set中,然后解析构造器的时候发现依赖A对象,去一级缓存获取是没有的(因为A对象实例化还未完成 未放入到一级缓存中)

      所以去实例化A 放入set的时候 发现已经存在及会抛出异常

    1 protected boolean isPrototypeCurrentlyInCreation(String beanName) {
    2         Object curVal = this.prototypesCurrentlyInCreation.get();
    3         return (curVal != null &&
    4                 (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    5     }
     1 protected void beforePrototypeCreation(String beanName) {
     2         Object curVal = this.prototypesCurrentlyInCreation.get();
     3         if (curVal == null) {
     4             this.prototypesCurrentlyInCreation.set(beanName);
     5         }
     6         else if (curVal instanceof String) {
     7             Set<String> beanNameSet = new HashSet<>(2);
     8             beanNameSet.add((String) curVal);
     9             beanNameSet.add(beanName);
    10             this.prototypesCurrentlyInCreation.set(beanNameSet);
    11         }
    12         else {
    13             Set<String> beanNameSet = (Set<String>) curVal;
    14             beanNameSet.add(beanName);
    15         }
    16     }

    对象在实例化之前会先调用isPrototypeCurrentlyInCreation方法,如果set中没有继续执行,实例化的时候调用beforePrototypeCreation方法 放入set中,在之后的循环依赖中 查询到set中有自己的beanName 则抛出异常

    那么spring对set方法注入 是如何解决循环依赖问题的呢,一样先说结论 是使用第三级缓存来解决的

    1. 在实例化bean A的时候 先去查看一级缓存中是否有(执行的方法getSingleton)

    并且会查询currentlyCreationSet中有没有

    2. 处理一下depend-on依赖 或者检查一下是否是抽象等等安全性校验 

    3.把单实例的A 的beanName放入currentlyCreationSet中

    4. 实例化A对象因为是set注入 所以这时使用的空参构造 去反射出实例对象 得到一个早期对象(未进行属性注入的对象,未执行init方法调用,未进行后处理器处理,早期对象和处理后的对象的内存地址是一样的aop对象除外)

    5. 早期实例A封装到objectFactory对象中,放入三级缓存

    6. 进行依赖注入 依赖注入的时候发现依赖Bean B 再去执行getBean方法 重复执行1-6步 拿到一个B的早期对象objectFactory 放入到三级缓存

    7. 处理B的依赖注入 发现B依赖了A  执行getbean方法 从1开始执行 去一级缓存查询没有 去currentlyCreationSet查询有A 去三级缓存中拿到A的早期实例 放入二级缓存清除三级缓存中的A早期实例并返回这个早期实例 B继续执行完成了B对象的实例 放入一级缓存中 清除掉二三级缓存相关数据

    8. 继续执行A的实例化步骤 A完成实例化 存入一级缓存 清除二三级缓存相关数据 

     1 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     2         Object singletonObject = this.singletonObjects.get(beanName);
     3         if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     4             synchronized (this.singletonObjects) {
     5                 singletonObject = this.earlySingletonObjects.get(beanName);
     6                 if (singletonObject == null && allowEarlyReference) {
     7                     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
     8                     if (singletonFactory != null) {
     9                         singletonObject = singletonFactory.getObject();
    10                         this.earlySingletonObjects.put(beanName, singletonObject);
    11                         this.singletonFactories.remove(beanName);
    12                     }
    13                 }
    14             }
    15         }
    16         return singletonObject;
    17     }
  • 相关阅读:
    使用python自带的http server共享文件
    vim常用快捷键与快捷操作
    wordpress 安装中文插件提示:To perform the requested action, WordPress needs to access your web server. Please enter your FTP cr
    python async异步编程,await后接task(三)
    虚拟机下ubuntu使用df命令查看磁盘空间小于实际空间
    python线程池
    使用Redis连接错误处理It was not possible to connect to the redis server(s);to create a disconnected multiple
    Sqlite数据库设置密码小工具
    [转]netcore一键部署到linux服务器以服务方式后台运行
    C#3种常见的定时器(多线程)
  • 原文地址:https://www.cnblogs.com/isnotnull/p/14300376.html
Copyright © 2020-2023  润新知