Spring框架解决循环依赖的方案——三级缓存机制

------------恢复内容开始------------

最近在复习Spring框架相关基础知识,面试题中经常出现的Spring框架解决循环依赖问题也看了很多网上的博客,以下是作为本人学习记录,如有不合理的地方,欢迎指正!

问题引入:

什么是循环依赖?循环依赖是指在对象创建过程中,对象的属性、或者构造器参数、或者方法参数依赖其他对象:比如A对象的setter方法的入参(也可以是构造器入参,也可以是接口方法入参)是对象B,而B对象中同样有A对象作为setter方法的入参,两者相互引用构成引用的闭环,这个例子就是最简单的循环依赖的案例;

代码如图:

@NoArgsConstructor
public class StudentA {

    private StudentB  studentB;


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

    }

    public void setStudentB(StudentB studentB) {
        this.studentB = studentB;
    }
}
@NoArgsConstructor
public class StudentB {

    private StudentA studentA;

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

    public void setStudentA(StudentA studentA) {
        this.studentA = studentA;
    }
}

 

Spring三级缓存是什么机制:在Spring中有三级缓存机制,一级缓存(singletonObjects)、二级缓存(earlySingletonObjects)、三级缓存(singletonFactories);

源码如下:

  private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);//一级缓存:主要存放的是已经实例化并且属性已经赋值的bean对象
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);//三级缓存:主要存放已经beanName和beanFactory(bean实例工厂)的对应关系
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);//二级缓存:主要存放已经实例化,但是对象属性还未赋值的bean对象

//getBean操作的核心源码
@Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//首先通过beanName去一级缓存查找
        Object singletonObject = this.singletonObjects.get(beanName);
        if(singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
//假如一级缓存不存在实例,进一步去二级缓存查找,如果此时找到直接进行返回
            singletonObject = this.earlySingletonObjects.get(beanName);
//如果未找到二级缓存未找到实例对象,检查allowEarlyReference是否为true,这个标志就是二级缓存解决循环依赖的关键,假如将这个标志设为false,
//那么spring解决循环依赖问题也就失效了(默认为true);这个标志叫做是否允许循环引用,也就是二级缓存中存放的仅仅是对象实例的早期引用,对象的属性还未做赋值
            if(singletonObject == null && allowEarlyReference) {
                Map var4 = this.singletonObjects;
                synchronized(this.singletonObjects) {

                    singletonObject = this.singletonObjects.get(beanName);
                    if(singletonObject == null) {
//这里二级缓存的查找实际查找的是对象实例的引用
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if(singletonObject == null) {
//重复一二级缓存查找的过程,如果还是没有则通过三级缓存创建beanName对应的singletonFactory(单例工厂)
                            ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                            if(singletonFactory != null) {
//由单例工厂创建一个bean的实例
                                singletonObject = singletonFactory.getObject();
//将该实例放入二级缓存(注:此时只是做了bean的实例化,未对属性赋值)
                                this.earlySingletonObjects.put(beanName, singletonObject);
//从三级缓存中移除该对象的单例工厂
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }

        return singletonObject;
    }

相信很多人看到上面源码的解释之后觉得二级缓存已经可以解决循环依赖了呀,那为啥还需要三级缓存呢?我一开始也这么认为,随后我看到一篇文章中解释了三级缓存存在的意义;

就是Spring的AOP面向切面编程,我们知道面向切面编程实现的原理就是动态代理,我们来看下getEarlyBeanReference方法的源码

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
        implements AutowireCapableBeanFactory {
        
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        // 1.如果bean不为空 && mbd不是合成 && 存在InstantiationAwareBeanPostProcessors
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                // 2.应用所有SmartInstantiationAwareBeanPostProcessor,调用getEarlyBeanReference方法
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    // 3.允许SmartInstantiationAwareBeanPostProcessor返回指定bean的早期引用
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                }
            }
        }
        // 4.返回要作为bean引用公开的对象,如果没有SmartInstantiationAwareBeanPostProcessor修改,则返回的是入参的bean对象本身
        return exposedObject;
    }
}

在开起AOP的情况下,

那么就是调用到AnnotationAwareAspectJAutoProxyCreator的父类的AbstractAutoProxyCreator的getEarlyBeanReference方法,对应的源码如下:
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
        
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        this.earlyProxyReferences.put(cacheKey, bean);
        // 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
        return wrapIfNecessary(bean, beanName, cacheKey);
    }
}

上面

wrapIfNecessary为Spring实现Bean代理的核心方法

  • wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference,另一处是postProcessAfterInitialization
  • 在wrapIfNecessary方法内部调用getAdvicesAndAdvisorsForBean()返回匹配当前Bean的所有Advice\Advisor\Interceptor,用于判断此该类是否需要创建代理。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
        
    /**
     * Spring实现Bean代理的核心方法。wrapIfNecessary在两处会被调用,一处是getEarlyBeanReference,
     * 另一处是postProcessAfterInitialization
     */
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        //已经被处理过
        // 1.判断当前bean是否在targetSourcedBeans缓存中存在(已经处理过),如果存在,则直接返回当前bean
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        //不需要被织入逻辑的
        // 2.在advisedBeans缓存中存在,并且value为false,则代表无需处理
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        //是不是基础的bean 是不是需要跳过的
        // 3.bean的类是aop基础设施类 || bean应该跳过,则标记为无需处理,并返回
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        // 返回匹配当前Bean的所有Advice\Advisor\Interceptor
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        // 5.如果存在增强器则创建代理
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //创建Bean对应的代理,SingletonTargetSource用于封装实现类的信息
            // 5.1 创建代理对象:这边SingletonTargetSource的target属性存放的就是我们原来的bean实例(也就是被代理对象),
            // 用于最后增加逻辑执行完毕后,通过反射执行我们真正的方法时使用(method.invoke(bean, args))
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            // 5.2 创建完代理后,将cacheKey -> 代理类的class放到缓存
            this.proxyTypes.put(cacheKey, proxy.getClass());
            // 返回代理对象
            return proxy;
        }
        //该Bean是不需要进行代理的,下次就不需要重复生成了
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
}回到上面的例子,我们对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。

三级缓存的意义何在?
就以我们上的A、B为例,其中A被AOP代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程

  Spring框架解决循环依赖的方案——三级缓存机制

 

 

假设不使用三级缓存,直接用二级缓存中,也是可以实现的,那么三级缓存搭配二级缓存的存在有什么意义呢?

 Spring框架解决循环依赖的方案——三级缓存机制

 

 

 

上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。对于整个A、B的创建过程而言,消耗的时间是一样的(所以常见的三级缓存提高了效率这种说法都是错误的)

上述这种情况下,差别就是在哪里创建代理。如果不用三级缓存,使用二级缓存,违背了Spring在结合AOP跟Bean的生命周期的设计!Spring结合AOP跟Bean的生命周期本身就是通过AbstractAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。

 

最后有关三级缓存作用的解释引用该博主的解释,原文地址为:https://www.jianshu.com/p/abda18eaa848

 
上一篇:【走近Spring】Spring的单例Bean定注册中心SingletonBeanRegistry详解


下一篇:Spring源码解析(三)-Spring事务原理分析