1、介绍几个概念
循环依赖
spring循环依赖解决不了构造器注入的
我们AB循环依赖问题只要A的注入方式是setter且singleton,就不会有循环依赖的问题。
默认的单例(singleton)的场景是支持循环依赖的,不报错
原型(prototype)的场景是不支持循环依赖的,会报错(因为spring是依靠三级缓存来解决循环依赖的,而原型是每次都创建一个新的对象,不会保存在缓存中)只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中
DefaultSingletonBeanRegistry
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/**
* Internal marker for a null singleton object:
* used as marker value for concurrent Maps (which don't support null values).
*/
protected static final Object NULL_OBJECT = new Object();
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
/** Cache of singleton objects: bean name --> bean instance */
// 一级缓存,缓存的是创建的bean实例
// 单例对象的缓存:bean 名称-bean实例,即所谓的单例池;表示已经经历了完整的生命周期的bean对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
// 三级缓存,它缓存的对象是ObjectFetcory,这个是个匿名内部类,
// 在解决循环依赖很重要,保持前后创建的对象是一致的,代理类或者常规类
// 单例工厂的高速缓存:bean名称--objectFactory
// 表示存放生成bean的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
// 二级缓存,它缓存的是还在创建过程中的对象
// 早期的单例对象的高速缓存:bean名称 -- bean实例
// 表示bean的生命周期还没有走完(bean的属性还未填充)就把这个bean存入该缓存中
// 也就是实例化但未初始化的bean放入该缓存里
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
}
循环依赖比较重要的代码
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 给缓存里面添加
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
// 回调
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
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);
}
}
}
下图是B创建完毕是,向一级缓存加数据的截图:
1、a创建过程中需要b,于是a将自己放到三级缓存里面,去实例化b
2、b实例化的时候发现需要a,于是b先查一级缓存,没有,再查询二级缓存,还是没有,再查三级缓存,找到了a,然后三级缓存里面的这个a放到二级缓存里面,并删除三级缓存里面的a
3、b顺利初始化完毕,将自己放到一级缓存里面(此时b里面的a依然事创建中状态),然后回来接着创建a,此时b已经创建结束,直接从一级缓存里面拿到b,然后完成创建,并将a自己放到一级缓存里面
循环依赖:(下面都是我看代码自己的见解)
1、首先是创建对象A,在创建完对象,给属性赋值的时候,由于要给B属性赋值(A还没有进行初始化工作,在赋值之前会把创建的A对象放到三级缓存中,缓存的是一个匿名内部类,这个内部类调用的时机是B给属性赋值的时候)
2、创建对象B,在创建完对象,给属性赋值的时候,由于要给A属性赋值(赋值之前会把创建的B对象放到三级缓存中,回调缓存在三级缓存的A的匿名对象,执行对象的创建过程(可能是代理类哦)),并初始化,保存在一级缓存中
A正在被创建,并赋值
3、给A对象赋值,并初始化,保存在一级缓存中
Spring解决循环依赖依靠的是bean的"中间态"这个概念,而这个中间态指的是已经实例化但还没有初始化但状态--》半成品。 假设A、B循环引用,实例化A的时候就将其放入到三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖A,这时从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果又AOP处理获取代理后的对象A,注入B,后剩下的流程 |
---|
![]() |
![]() |
|
常问的问题
1、为什么使用构造器注入属性的时候不能解决循环依赖的问题?
原因在于此方式是实例化和初始化分开(在进行实例化的时候不必给属性赋值)来操作,将提前实例化好的对象提前暴露出去,供别人调用,而使用的构造器的时候,必须调用构造器方法了,没有构造器方法无法完成对象实例化操作,也就无法创建对象,那么永远会陷入死循环中。
2、一级缓存能不能解决此问题?
不能,在三个级别的缓存中,放置的对象是有区别的(1、是完成实例化且初始化的对象;2、是实例化但是未初始化的对象;),如果只有一级缓存,就有可能取到实例化但未初始化的对象,属性值都为空,肯定有问题
不能,如果只有一级缓存,那么就意味着完全状态和非完全状态的对象都存在,如果此时需要获取某个对象,恰巧获取到非完全状态的对象,怎么办?(没有办法区分,非完全状态的对象(属性没有赋值完,对象还不能使用))可以添加二级缓存(可以多添加几个缓存来满足)
3、二级缓存能不能解决?为什么使用三级缓存?
理论上来说是可以解决循环依赖的问题,但是要注意了:思考--为什么要在三级缓存中放匿名内部类?
本质在于为了创建代理对象,例如现在有A类,需要生成代理对象,A需不需要实例化,需要
如果在A类上配置AOP,是否需要生成一个代理对象?
我们需要三级缓存的最主要意义在于,你所需要的类有可能是简单对象(实例化、初始化),也可能是需要进行代理的对象,当我们向三级缓存中放置匿名类部类的时候,可以在获取的时候决定到底是简单对象,还是代理对象,这就是三级缓存的意义!!!
在三级缓存中放置的是,生成具体对象的一个匿名内部类,这个匿名内部类可能是代理类,也可能是普通的实例对象,而使用三级缓存就保证了不管是否需要代理对象,都保证了使用的是一个对象,而不会出现,前面使用普通bean,后面使用代理类。