一、什么是spring循环依赖?
- 简单来说就是图中所示,beanA依赖beanB,beanB依赖beanA,就形成了循环依赖。多个bean之间的闭合单项闭环依赖也是循环依赖。
二、要清楚怎么解决循环依赖,先清楚spring是怎么创建我们的bean。
- spring中创建bean的方式
- spring中通过beanFactory创建bean,ApplicationContext继承BeanFactory,classpathXmlApplicationContext和AnnotationConfigApplicationContext分别实现ApplicationContext。
3、他们之间的关系如下图,当一个类试图初始化的时候,ApplicationContext相当于设计师,将一个Class转化成图纸提供给BeanFactory,BeanFactory相当于工厂,负责bean的整个创建过程。
4、总结一下,BeanFactory和ApplicationContext的区别
5、bean实例化的过程,通过beanDefine里面的classObject.newInstance实例化,通过反射进行赋值,初始化完成后添加进单例池,是一个currentHashMap,线程安全。map的key就是实例的名称,通过map.get(“名称”)获取。
6、bean的后置处理器beanPostProcessor,在bean的创建中总共会调用八次。
7、Aop底层是通过动态代理实现的,在bean的实例化过程中,在那个过程创建动态代理?(Aop使用CGLIB还是JDK动态代理主要看是不是接口)
- JDK动态代理是面向接口,在创建代理实现类时比CGLib要快,创建代理速度快。
- CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败),在创建代理这一块没有JDK动态代理快,但是运行速度比JDK动态代理要快。
- 使用注意:
如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作(Spirng默认采用JDK动态代理实现机制),如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理。
答案:在bean初始化之后创建动态代理。
三、spring如何解决循环依赖问题
- 从上面的知识我们知道,bean的创建主要有三个过程,实例化,属性赋值,和初始化。循环依赖主要发生在属性赋值部分,也就是构造器循环依赖和field循环依赖。先看看循环依赖中创建bean的过程吧。
- 我们从源码中看看整个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);
- 从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);
}
- 到了这里,相信已经比较清楚了,我们来分析一下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的创建。