Spring Bean循环依赖学习与探究

本文参考:

画图带你彻底弄懂三级缓存和循环依赖的问题

Spring 三级缓存解决bean循环依赖,为何用三级缓存而非二级_笑矣乎的博客-CSDN博客

Spring为何需要三级缓存解决循环依赖,而不是二级缓存?_石杉的架构笔记的博客-CSDN博客

原理学习

主要的三级缓存工作机理学习参考 画图带你彻底弄懂三级缓存和循环依赖的问题

文章中解决了“是什么”的问题,这里主要梳理下”为什么“的问题,即为什么非得是三级缓存才能解决呢?也就是上面积累下来的问题

七、不用三级缓存,用二级缓存能不能解决循环依赖

遇到这种面试题,你就跟面试官说,如果行的话,Spring的作者为什么不这么写呢?

哈哈,开个玩笑,接下来说说到底为什么不行。

这里我先说一下前面没提到的细节,那就是通过ObjectFactory获取的Bean可能是两种类型,第一种就是实例化阶段创建出来的对象,还是一种就是实例化阶段创建出来的对象的代理对象。至于是不是代理对象,取决于你的配置,如果添加了事务注解又或是自定义aop切面,那就需要代理。这里你不用担心,如果这里获取的是代理对象,那么最后完全创建好的对象也是代理对象,ObjectFactory获取的对象和最终完全创建好的还是同一个,不是同一个肯定会报错,所以上面的理论依然符合,这里只是更加的细节化。

有了这个知识点之后,我们就来谈一下为什么要三级缓存。

第一级缓存,也就是缓存完全创建好的Bean的缓存,这个缓存肯定是需要的,因为单例的Bean只能创建一次,那么肯定需要第一级缓存存储这些对象,如果有需要,直接从第一级缓存返回。那么如果只能有二级缓存的话,就只能舍弃第二级或者第三级缓存。

假设舍弃第三级缓存

舍弃第三级缓存,也就是没有ObjectFactory,那么就需要往第二缓存放入早期的Bean,那么是放没有代理的Bean还是被代理的Bean呢?

1)如果直接往二级缓存添加没有被代理的Bean,那么可能注入给其它对象的Bean跟最后最后完全生成的Bean是不一样的,因为最后生成的是代理对象,这肯定是不允许的;

2)那么如果直接往二级缓存添加一个代理Bean呢?

  • 假设没有循环依赖,提前暴露了代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错,
  • 假设没有循环依赖,使用了ObjectFactory,那么就不会提前暴露了代理对象,到最后生成的对象是什么就是什么,就不会报错,
  • 如果有循环依赖,不论怎样都会提前暴露代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错

通过上面分析,如果没有循环依赖,使用ObjectFactory,就减少了提前暴露代理对象的可能性,从而减少报错的可能。

假设舍弃第二级缓存

假设舍弃第二级缓存,也就是没有存放早期的Bean的缓存,其实肯定也不行。上面说过,ObjectFactory其实获取的对象可能是代理的对象,那么如果每次都通过ObjectFactory获取代理对象,那么每次都重新创建一个代理对象,这肯定也是不允许的。

从上面分析,知道为什么不能使用二级缓存了吧,第三级缓存就是为了避免过早地创建代理对象,从而避免没有循环依赖过早暴露代理对象产生的问题,而第二级缓存就是防止多次创建代理对象,导致对象不同。

源码溯源

为了从源码角度解决“不用三级缓存,用二级缓存能不能解决循环依赖”的问题,继续去找各种文章去解答:

参考 Spring 三级缓存解决bean循环依赖,为何用三级缓存而非二级_笑矣乎的博客-CSDN博客

DefaultSingletonBeanRegistry.java中有三级缓存,以及如何获取的源码

/**
	 * Return the (raw) singleton object registered under the given name.
	 * <p>Checks already instantiated singletons and also allows for an early
	 * reference to a currently created singleton (resolving a circular reference).
	 * @param beanName the name of the bean to look for
	 * @param allowEarlyReference whether early references should be created or not
	 * @return the registered singleton object, or {@code null} if none found
	 */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
          // 查询一级缓存
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
            // 若一级缓存不存在,查询二级缓存
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
              // 若二级缓存不存在,查询三级缓存
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject);
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

AbstractBeanFactory下面有一个createBean()的中心方法,其核心实现方法doCreateBean(),可以看出来是在类实例化以后,属性填充之前,先检查是否允许提前暴露,如果允许则将其加入三级缓存工厂中
在这里插入图片描述

文章作者认为二级缓存不能解决问题,主要是因为在AOP的场景下,注入到其他bean的,不是AOP代理对象,而是原始对象。但其实这也不完全对,完全可以让AOP代理对象注入到二级缓存中,但是这有悖于AOP的设计原理。AOP就是要等Bean实例化,属性填充,初始化完成之后才去 生成AOP代理对象的,如果使用二级缓存,又要将AOP代理对象注入的话,就需要强行将AOP的代理工作提前到早期暴露实例之前。

但是上面总归还是理论性的知识,在源码层面上,三级缓存究竟做了什么还需要探究,参考

Spring为何需要三级缓存解决循环依赖,而不是二级缓存?_石杉的架构笔记的博客-CSDN博客

三级缓存中key-value加入的是一个beanName 和一个ObjectFactory的工厂类

/**
	 * Add the given singleton factory for building the specified singleton
	 * if necessary.
	 * <p>To be called for eager registration of singletons, e.g. to be able to
	 * resolve circular references.
	 * @param beanName the name of the bean
	 * @param singletonFactory the factory for the singleton object
	 */
	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);
			}
		}
	}

在前面获取的时候,也是直接调用ObjectFactory的工厂类的getObject方法

// 若二级缓存不存在,查询三级缓存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
  singletonObject = singletonFactory.getObject();
  this.earlySingletonObjects.put(beanName, singletonObject);
  this.singletonFactories.remove(beanName);
}

最终调用getEarlyBeanReference方法

/**
	 * Obtain a reference for early access to the specified bean,
	 * typically for the purpose of resolving a circular reference.
	 * @param beanName the name of the bean (for error handling purposes)
	 * @param mbd the merged bean definition for the bean
	 * @param bean the raw bean instance
	 * @return the object to expose as bean reference
	 */
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

在这里插入图片描述
从getSingleton()中可以看出,在AOP的情况下,每次singletonFactory.getObject()取出来的都是不同的Aservice代理类,由于Aservice是单例的,这样子显然不行。因此将singletonFactory.getObject()取出来的东西放入二级缓存中,key还是beanName,这样子别的Bean在取的时候直接就可以从二级缓存中拿出来,不必每次调用singletonFactory.getObject()产生一个新的代理对象。

总而言之,在没有AOP的情况下,确实可以通过两级缓存解决循环依赖问题,但是如果加上AOP,两级缓存就不行了。

最后再复习下CGLib动态代理的用法:

package com.jxz.AopTest;

import net.sf.cglib.proxy.Enhancer;

/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/9/16
 */
public class CGlibTest {
    public static void main(String[] args) {
        WeakService weakService = new WeakService();

        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(weakService.getClass().getClassLoader());
        enhancer.setSuperclass(weakService.getClass());
        enhancer.setCallback(new JXZMethodInterceptor());

        // 创建代理类
        WeakService proxy = (WeakService)enhancer.create();
        proxy.f("I'm jxz");
    }
}
package com.jxz.AopTest;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/9/16
 */
public class JXZMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("增强前...");
        // 这里调用父类方法,不然一直调用本方法死循环了
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("增强后...");
        return result;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网民工蒋大钊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值