什么是循环引用?
比如对象A有属性B,对象B有属性A,这就是循环引用。
整个Spring Bean的创建分为:解析、注册、创建。循环引用则发生在bean的创建过程中。
废话不多上,直接上代码,以下是bean创建的核心代码:
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
......
......
// 从缓存中取是否已经有被创建过的单态类型的Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 获取bean实例的对象,如果是FactoryBean,则为bean实例本身或其创建的对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else{
......
// 根据指定Bean名称获取合并的Bean定义,在Bean继承时 ,合并子类跟父类的公共属性
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
.......
// 创建单态模式Bean的实例对象
if (mbd.isSingleton()) {
// 使用匿名内部类创建Bean实例对象,为什要用这样创建方式可以忽略,设计模式的一种
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 创建Bean实例对象
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 从容器中清除所有Bean缓存跟bean之间的依赖关系
destroySingleton(beanName);
throw ex;
}
}
});
// 获取Bean的实例对象
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
......
......
}
return (T) bean;
}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
//从单例对象缓存中获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
boolean newSingleton = false;
try {
//初始化bean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (Exception ex) {
throw ex;
}
if (newSingleton) {
//添加单例对象缓存
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
上面是创建bean的入口方法,逻辑很简单,先去容器中获取如果有,就直接返回,没有就创建。这里我们重点关注getSingleton(beanName)和createBean(beanName, mbd, args)这两个方法。记住addSingleton(beanName, singletonObject);方法
这里可以先思考一个问题:
1.首先我们调用getSingleton(beanName)去获取,那么在什么情况下能获取到呢?
/ *
* @param beanName 要查找的bean的名称
* @param allowEarlyReference 是否允许创建早期引用(第一步获取的时候永远是true)
*/
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);
}
以下名词解释:
// 单例对象的缓存: (简称一级缓存)
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
// 单例工厂缓存:(简称三级缓存)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
// 早期单例对象缓存: (简称二级缓存)
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
实际上真没有什么三级缓存,也没有真正顺序上的等级之分,这里只是为了简化记忆,包括在下文我会直接引用简称。
如果从程序正确执行的逻辑上来讲,这段代码先从一级缓存中获取,一级缓存没有从二级缓存中获取,二级没有从三级缓存中拿。如果三级缓存中有,将从三级缓存中获取实例对象,同时删除一级缓存,添加二级缓存。
这段代码会出现以下两种情形:
- 第一次创建,一级、二级、三级缓存都是空,所有才会执行接下来的
createBean(beanName,mbd,args)
方法进行创建。 - 第二次创建,一级、二级缓存为空,三级缓存不为空。只有在循环依赖的情况下才有第二次创建。
如果第二次创建成功(能成功解析循环引用),那么必定是从三级缓存中获取实例对象,也就是执行这块代码:
if (singletonFactory != null) {
//通过单例工厂获取单例对象
singletonObject = singletonFactory.getObject();
//添加到早期单例缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//从单例工厂缓存删除
this.singletonFactories.remove(beanName);
}
以下有几个疑问?
- 为什么第二次创建,一级、二级缓存为空,三级缓存不为空?
- 为什么从三级缓存中获取bean实例后,要添加二级缓存,清除一级缓存呢?
- 为什么说在第一次创建时,如果不添加三级缓存那么必定无法解析循环引用?
- 在一次创建时,会在哪种情形下添加不了三级缓存呢?
记住:从三级缓存获取的bean实例的这行代码 singletonObject = singletonFactory.getObject();
这也是解析循环引用的关键
带着以上几个疑问我们继续往下看,以下是createBean(beanName, mbd, args)方法
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
throws BeanCreationException {
......
......
// 忽略 aop动态代理就是从这里进入并返回一个代理对象(我们常用的aop切面功能就是这里实现的)
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
......
// 创建bean的入口
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
以上我们只需关注doCreateBean(beanName, mbdToUse, args);这个方法,具体代码如下:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd,
final Object[] args) throws BeanCreationException {
// 创建实例对象
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
// 获取创建的实例对象
final Object bean = (instanceWrapper != null
? instanceWrapper.getWrappedInstance()
: null);
.....
.....
// 忽略 调用PostProcessor后置处理器
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
.....
.....
// 是否曝光早期引用 条件是:单例 && 允许解析循环依赖 && 当前bean正在创建中
boolean earlySingletonExposure = (mbd.isSingleton()
&& this.allowCircularReferences
&& isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// 记住这个方法
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
// 以下这行代码可以理解 return bean;
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
Object exposedObject = bean;
// 属性注入
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 初始化Bean对象
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
.....
.....
return exposedObject;
}
doCreateBean()
执行步骤如下:
- 创建实例 ,创建方式有空参、有参(自动装配)、工厂方法,一般使用无参构造。
- 添加缓存,将创建好的bean实例放入三级缓存中。
- 属性注入,给实例对象中的属性赋值。
- 初始化,在调用
init-method
属性指定的初始化方法前后作一些处理
下面我们主要看添加缓存的代码:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
//添加到单例工厂缓存
this.singletonFactories.put(beanName, singletonFactory);
//从早期单例对象缓存删除
this.earlySingletonObjects.remove(beanName);
//添加到已注册单例缓存
this.registeredSingletons.add(beanName);
}
}
}
现在我们已经知道三级缓存是如何设置的了,注意:添加三级缓存是在实例化后,属性注入之前 。
- 那么为什么要删除二级缓存呢?
答案就是在第二次创建时重新调用getSingleton(beanName)方法时,要想获取三级缓存,二级缓存必须清空。
- 那还记得刚开始从三级缓存获取的那行代码么,
singletonObject = singletonFactory.getObject();
其中getObject()
方法将获取到第一次创建的bean实例
@Override
public Object getObject() throws BeansException {
// 以下这行代码可以理解 return bean;
return getEarlyBeanReference(beanName, mbd, bean);
}
当上面四个步骤都执行无误,也意味着createBean(beanName, mbd, args)方法执行完毕。
我们将再次回到文章最开始的这段代码:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
//从单例对象缓存中获取
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
boolean newSingleton = false;
try {
//初始化bean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (Exception ex) {
throw ex;
}
if (newSingleton) {
//添加单例对象缓存
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
添加一级缓存,清除二级、三级缓存
//添加单例对象缓存
addSingleton(beanName, singletonObject);
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//添加到单例对象缓存
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
//删除单例工厂缓存
this.singletonFactories.remove(beanName);
//删除早期单例对象缓存
this.earlySingletonObjects.remove(beanName);
//添加已注册单例缓存
this.registeredSingletons.add(beanName);
}
}
分析完了,循环引用就是这么解决的。
如果面试官问你为什么使用构造函数依赖注入不能解决循环引用,你就跟他说构造函数注入可以解决循环引用啊,告诉他将下面这两行代码注释就行了。(不过这样循环引用是解决了,但是注入了个寂寞,哈哈哈)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
.....
//beforeSingletonCreation(beanName);
.....
//afterSingletonCreation(beanName);
}
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd,
Object[] args) {
.......
/*if (ctors != null
|| mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR
|| mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}*/
}