• 【闲聊杂谈】源码追踪Spring的三级缓存及循环依赖


    在上一篇中我们详细讨论了Spring中Bean的生命周期,对三级缓存和循环依赖的问题有过一些比较粗略的讲解。那么这篇结合自定义的代码和Spring的源码一起深入了解下Spring的三级缓存及循环依赖问题。

    首先我们要知道什么是循环依赖问题?

    此时图中是一个闭环。如果想解决这个闭环问题,那么必须要保证不会出现第二次创建A对象这个步骤,也就是说从容器中必须能够获取到A这个对象。在Spring中,对象的创建分为实例化和初始化,实例化完成初始化尚未开始的时候,已实例化的对象是可以直接给其它对象引用的。所以可以通过把已实例化未初始化的对象提前暴露出去,让其它对象可以引用,这样就解决了这个闭环的问题,而已实例化未初始化的对象就放在缓存中。

    Spring三级缓存分别是
    一级缓存:
    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);

    三级缓存的执行流程在DefaultSingletonBeanRegistry类中的getSingleton方法
    先从一级缓存中取,一级缓存总没有再从二级缓存中取,二级缓存总没有再从三级缓存中取

    如果发生循环依赖的对象,不需要代理的话,只需要二级缓存足矣;但是如果存在代理之后,必须要使用三级缓存才能解决。

    好,接下来自定义类和xml文件进行Debug的跟踪:

    来到preInstantiateSingletons方法中

    // 将所有BeanDefinition的名字创建一个集合
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // 触发所有非延迟加载单例Bean的初始化,遍历集合的对象
    for (String beanName : beanNames) {
        // 合并父类BeanDefinition
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        // 需满足:抽象&&单例&&非懒加载
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            // 判断是否实现了FactoryBean接口
            if (isFactoryBean(beanName)) {
                .....
            } else {
                // 如果beanName对应的bean不是FactoryBean,而只是普通的Bean
                // 通过beanName获取bean实例
                getBean(beanName);
            }
    进入getBean方法中,再进入doGetBean方法中,这个方法中有一个很重要的判断方法getSingleton,这个就是从一级缓存中判断下有没有这个对象

    返回到doGetBean方法中
    // 当对象都是单例的时候尝试解决循环依赖的问题
    // 但是原型模式下如果存在循环依赖的情况
    // 则直接抛出异常
    if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }

    代码继续往下走
    // 创建Bean的实例对象
    if (mbd.isSingleton()) {
        // 返回以beanName的原始单例对象,如果尚未注册
        // 则使用singletonFactory创建并注册一个对象
        sharedInstance = getSingleton(beanName, () -> {
            try {
                // 为给定的合并后BeanDefinition和参数创建一个Bean实例
                return createBean(beanName, mbd, args);
            } catch (BeansException ex) {
                // Explicitly remove instance from singleton cache: It might have been put there
                // eagerly by the creation process, to allow for circular reference resolution.
                // Also remove any beans that received a temporary reference to the bean.
                destroySingleton(beanName);
                throw ex;
            }
        });
        
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    当调用getSingleton这个方法的时候,第一个参数是beanName,第二个参数是一个匿名内部类,就是一个lambda表达式。进入方法中

    进入到singletonFactory.getObject()方法中,继续进入到createBean方法中,找到doCreateBean方法进入 

    // 根据执行bean使用对应的策略创建新的实例
    // 比如:工厂方法、构造函数主注入、简单初始化等
    instanceWrapper = createBeanInstance(beanName, mbd, args);

    // 从包装类中获取原始Bean
    Object bean = instanceWrapper.getWrappedInstance();

    此时的A是只是一个完成了实例化,尚未初始化的半成品。

    代码继续往下走
    // 判断当前Bean是否需要提前曝光:单例&&循环依赖&&当前bean正在创建中,检测循环依赖
    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");
        }
        
        // 为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    进入addSingletonFactory方法
    synchronized (this.singletonObjects) {
        // 如果单例对象的高速缓存[beanName-bean实例]中没有beanName对应的这个对象
        if (!this.singletonObjects.containsKey(beanName)) {
            // 将beanName、singletonFactory放到单例工厂的缓存[beanName-ObjectFactory]中
            this.singletonFactories.put(beanName, singletonFactory);
            // 从早去单例对象的高速缓存[beanName-bean实例]中移除beanName的相关缓存对象
            this.earlySingletonObjects.remove(beanName);
            // 将beanName添加已注册的单例集合中
            this.registeredSingletons.add(beanName);
        }
    }

    代码继续往下走回到doCreateBean方法中
    // 对bean的属性进行填充,将各个属性值注入
    // 如果存在依赖其它bean的属性,则会递归初始化依赖的bean
    populateBean(beanName, mbd, instanceWrapper);

    进入populateBean方法中
    // 应用给定的属性值,解决任何在这个bean工厂运行时其它bean的引用
    // 必须使用深拷贝,所以我们不会永久的修改这个属性
    applyPropertyValues(beanName, mbd, bw, pvs);

    进入到applyPropertyValues方法中
    for (PropertyValue pv : original) {
        .....
    } else {
        // 获取属性的名字
        String propertyName = pv.getName();
        // 获取未经类型转换的值
        Object originalValue = pv.getValue();
        // AutowiredPropertyMarker.INSTANCE:自动生成标记的规范实例
        if (originalValue == AutowiredPropertyMarker.INSTANCE) {
            .....
        }
        // 交由valueResolver根据pv解析出originalValue所封装的对象
        Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
        .....
    }

    进入到resolveValueIfNecessary方法中
    if (value instanceof RuntimeBeanReference) {
        RuntimeBeanReference ref = (RuntimeBeanReference) value;
        // 解析出对应ref所封装的Bean元信息[beanName,bean类型]的Bean对象
        return resolveReference(argName, ref);
    }

    进入到resolveReference方法中
    resolvedName = String.valueOf(doEvaluate(ref.getBeanName()));
    // 获取resolvedName的Bean对象,也就是从容器中获取
    bean = this.beanFactory.getBean(resolvedName);

    进入到getBean方法中,继续进入到doGetBean方法中
    // 提前检查单例缓存中是否有手动注册的单例对象
    // 跟循环依赖有关联
    Object sharedInstance = getSingleton(beanName);

    进入到getSingleton方法中,继续进入getSingleton方法中
    // 从单例对象缓存中获取beanName对应的单例对象
    Object singletonObject = this.singletonObjects.get(beanName);
    // 如果单例对象缓存中没有,且该beanName对应的单例bean正在创建中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 从earlySingletonObjects缓存中获取单例对象
        // 获取的这个对象是通过提前曝光的ObjectFactory创建出来的
        // 还未进行属性填充等操作
        singletonObject = this.earlySingletonObjects.get(beanName);
        // 如果earlySingletonObjects缓存中也没有
        // 并且也允许创建的话
        if (singletonObject == null && allowEarlyReference) {
            .....
        }
        
        
    代码继续往下走,回到doGetBean方法中
    // 如果不是做类型检查,那么表示要创建bean,此处在结合中做一个记录
    if (!typeCheckOnly) {
        // 为beanName标记为已经创建,或将要创建
        markBeanAsCreated(beanName);
    }

    代码继续往下走,来到getSingleton方法,继续进入,会发现B也将创建一个半成品对象。然后对B进行属性填充,就需要用到A对象。A对象在之前已经创建出来,有在三级缓存中的任何一级缓存中吗?显然并没有。代码继续往下走,再次来到getSingleton方法中
    singletonObject == null为true
    allowEarlyReference也为true
    刚刚跳过的这段代码开始进入执行
    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) {
                        // 当某些方法需要提前初始化的时候则会调用addSingletonFactory方法
                        // 将对应的ObjectFactory初始化策略存储在singletonFactories
                        // 这便是从三级缓存中取
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            // 如果存在单例对象工厂,则通过工厂创建一个单例对象
                            singletonObject = singletonFactory.getObject();
                            // 记录在缓存中,二级缓存和三级缓存的对象不能同时存在
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            // 从三级缓存中移除
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }

    singletonObject = singletonFactory.getObject()点F7进入就会来到addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))这个方法中的lambda表达式() -> getEarlyBeanReference(beanName, mbd, bean)。继续往下执行,回到getSingleton方法中,会发现将半成品的A返回了

    代码继续往下走,回到doCreateBean方法中,就会发现B中的a属性已经被A赋值了

    代码继续往下走,来到getSingleton方法中
    // 这个单例对象是不是新对象
    if (newSingleton) {
        // 将beanName和singletonObject的映射关系添加到该工厂的单例单例缓存中
        addSingleton(beanName, singletonObject);
    }

    进入addSingleton方法中
    synchronized (this.singletonObjects) {
        // 放到一级缓存中
        this.singletonObjects.put(beanName, singletonObject);
        // 从二级缓存中移除
        this.singletonFactories.remove(beanName);
        // 从三级缓存中移除
        this.earlySingletonObjects.remove(beanName);
        // 将beanName添加到已注册的单例集中
        this.registeredSingletons.add(beanName);
    }

    此时B对象已经完成,回到A对象的赋值操作中,将B赋值给A对象的a属性。那么对A继续刚刚和B一样的操作,放到一级缓存中,从二级缓存中移除,从三级缓存中移除。这就是完整的一个创建过程。

    那么,从上述的创建过程中,三级缓存是怎么流转的呢?
    三级缓存中,并不是严格的从三级到二级到一级。有可能在三级和一级中有对象,也有可能再一二三级中都有对象。

    如何改变源码中的三级缓存引用关系,将三级缓存改为二级缓存?
    1、在doCreateBean中有添加lambda表示式到三级缓存的代码,要改成直接放到二级缓存;
    2、直接修改getSingleton方法,将三级缓存去掉;

  • 相关阅读:
    2023年中国轮胎模具需求量、竞争格局及行业市场规模分析[图]
    Xray 漏洞扫描工具使用方法
    java计算机毕业设计ssm网络相册设计sepo8(附源码、数据库)
    CVE-2017-15715 apache换行解析&文件上传漏洞
    Hadoop 2.0:主流开源云架构(四)
    Ansys Zemax | 手机镜头设计 - 第 3 部分:使用 STAR 模块和 ZOS-API 进行 STOP 分析
    Kubernetes-三大开放接口-初见
    路由器基础(十一):ACL 配置
    OpenCV中的RGB与YUV转换
    【无标题】
  • 原文地址:https://blog.csdn.net/FeenixOne/article/details/125426493