怎么解决spring中(单例的Bean)循环依赖?

一、什么是spring循环依赖?

  1. 简单来说就是图中所示,beanA依赖beanB,beanB依赖beanA,就形成了循环依赖。多个bean之间的闭合单项闭环依赖也是循环依赖。
    怎么解决spring中(单例的Bean)循环依赖?_第1张图片

二、要清楚怎么解决循环依赖,先清楚spring是怎么创建我们的bean。

  1. spring中创建bean的方式怎么解决spring中(单例的Bean)循环依赖?_第2张图片
  2. spring中通过beanFactory创建bean,ApplicationContext继承BeanFactory,classpathXmlApplicationContext和AnnotationConfigApplicationContext分别实现ApplicationContext。
    怎么解决spring中(单例的Bean)循环依赖?_第3张图片
    3、他们之间的关系如下图,当一个类试图初始化的时候,ApplicationContext相当于设计师,将一个Class转化成图纸提供给BeanFactory,BeanFactory相当于工厂,负责bean的整个创建过程。
    怎么解决spring中(单例的Bean)循环依赖?_第4张图片
    4、总结一下,BeanFactory和ApplicationContext的区别
    怎么解决spring中(单例的Bean)循环依赖?_第5张图片

5、bean实例化的过程,通过beanDefine里面的classObject.newInstance实例化,通过反射进行赋值,初始化完成后添加进单例池,是一个currentHashMap,线程安全。map的key就是实例的名称,通过map.get(“名称”)获取。
怎么解决spring中(单例的Bean)循环依赖?_第6张图片
6、bean的后置处理器beanPostProcessor,在bean的创建中总共会调用八次。
怎么解决spring中(单例的Bean)循环依赖?_第7张图片
7、Aop底层是通过动态代理实现的,在bean的实例化过程中,在那个过程创建动态代理?(Aop使用CGLIB还是JDK动态代理主要看是不是接口)

  • JDK动态代理是面向接口,在创建代理实现类时比CGLib要快,创建代理速度快。
  • CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败),在创建代理这一块没有JDK动态代理快,但是运行速度比JDK动态代理要快。
  • 使用注意:
    如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制),如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
    答案:在bean初始化之后创建动态代理。
    怎么解决spring中(单例的Bean)循环依赖?_第8张图片

三、spring如何解决循环依赖问题

  1. 从上面的知识我们知道,bean的创建主要有三个过程,实例化,属性赋值,和初始化。循环依赖主要发生在属性赋值部分,也就是构造器循环依赖和field循环依赖。先看看循环依赖中创建bean的过程吧。
    怎么解决spring中(单例的Bean)循环依赖?_第9张图片
  2. 我们从源码中看看整个bean的创建过程。我们创建bean的过程,最先想到的是从缓存中获取bean,而这个缓存就是singletonObjects,这里我们需要了解一下三级缓存的知识,源码中三级缓存就是三个Map,Map的Key就是bean的名称,源码如下。
/** 一级缓存 单例对象的cache Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
 
/** 三级缓存 单例对象工厂的cache Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
 
/** 二级缓存 提前暴光的单例对象的Cache 。【用于检测循环引用,与singletonFactories互斥】 Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
  1. 从singletonObjects缓存中获取bean的主要方法如下
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     
        synchronized (this.singletonObjects) {
     
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
     
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
     
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
  1. 到了这里,相信已经比较清楚了,我们来分析一下A和B 两个Bean循环依赖情况下,spring是怎么创建我们的bean的。
  • 创建A对象,调用getBean方法,然后调用 doGetBean,进来以后首先会执行 transformedBeanName 找别名,看你的 Bean 上面是否起了别名。然后进行很重要的一步,执行getSingleton,去单例缓存池中获取bean。
Object singletonObject = this.singletonObjects.get(beanName);

然后判断是否获取到,没获取到就去二级缓存中获取

if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))
            singletonObject = this.earlySingletonObjects.get(beanName);

二级缓存中也没有获取到,就去三级缓存,实例工厂缓存中获取,allowEarlyReference是是否允许从实例工厂缓存中获取实例,默认true

if (singletonObject == null && allowEarlyReference) 
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);

三级缓存中获取到实例,就把实例存入二级缓存中,并从实例工厂移除实例。
从上面的bean创建过程可以看出,最后一步,是从三级缓存中singletonFactories获取bean。

public interface ObjectFactory<T> {
     
    T getObject() throws BeansException;
}

这个接口执行的下面的逻辑

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);
        }
    }
}
  • 到这里,spring解决单例的属性的循环依赖的逻辑就很清晰了,A bean依赖B bean,那么在创建A bean的时候,调用createBeanInstance后,就会调用addSingleFactory把这个早期的对象放入三级缓存池中,接下来进行populateBean,给bean赋值的时候,发现依赖B bean就会去执行B bean的创建过程,同样到了populateBean属性赋值的时候发现依赖A bean 就会先后调用一二三级缓存,最后在三级缓存中发现A bean的早期对象,B bean接下来会执行完整个bean创建 流程,创建bean成功,接下来返回A bean的创建过程,完成A 和 B bean的创建。

你可能感兴趣的:(循环依赖,java)