spring 三级缓存源码分析

本文详细剖析Spring中如何通过三级缓存机制解决循环依赖问题,重点讲解了代理对象在处理aop与循环依赖中的角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spring中对于循环依赖的解决采用了三级缓存机制,即:

Map<String, Object> singletonObjects              //第一级缓存,存放初始化完成的bean
Map<String, Object> earlySingletonObjects         //第二级缓存,存放半成品bean
Map<String, ObjectFactory<?>> singletonFactories  //第三级缓存,存放bean的工厂类

下面通过源码分析创建循环依赖bean的流程,假设存在循环依赖的bean如下:

@Service
class BeanA {

  @Autowired
  BeanB beanB;

  public void test() {
     System.out.println("test");
  }
}

@Service
class BeanB {

  @Autowired
  BeanA beanA;
}

@Aspect
@Component
public class UserAspect {

    @Pointcut("execution(* com.tao.spring.*.*(..))")
    public void addLog() {}

    @Before("addLog()")
    public void before() {
        System.out.println("before");
    }
}

首先从获取BeanA开始,创建bean的入口处在AbstractBeanFactory.doGetBean:

        该方法中试图通过getSingleton获取bean实例,看下该方法内容:

public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
}

@Nullable
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;
}

          首先从一级缓存中获取,获取不到再从二级缓存中获取,二级缓存中没有再从三级缓存中获取。开始时缓存中都没有BeanA、BeanB的信息,故获取不到bean实例,准备创建bean,入口在AbstractAutowireCapableBeanFactory.doCreateBean,下面来分析下该流程:

          首先会创建raw bean,此时创建的beanA只是一个空壳,其中的属性为空

           接下来构造一个bean的工厂并缓存到第三级缓存中,执行到这一步时,第一、第二级缓存仍为空,第三级缓存中存储了BeanA的工厂对象。

           接着走到populateBean方法,该方法需要初始化beanA的成员,由于beanA中包含beanB,故需要创建beanB,继续走到doCreateBean方法,此时传入的beanName参数为beanB,仍然会创建一个raw bean,此时创建的beanB只是一个空壳,其中的属性为空

           

           同样会将beanB的工厂对象缓存到第三级缓存中

           接下来继续走到populateBean方法中,即beanB需要获取注入的beanA对象,最终走到getSingleton方法中:

          beanB试图通过该方法获取beanA的实例,由于第三级缓存中有beanA的工厂对象,故通过工厂对象创建beanA的实例并加入到第二级缓存中,注意此时创建的beanA实例对象是经过spring代理的对象,beanA的中beanB此时仍然为null,执行完方法populateBean之后,beanB已经包含了beanA的实例对象(虽然此时beanA尚未初始化完成,但这并不妨碍beanB的初始):

       beanB初始化完成之后,加入到一级缓存中

       beanA执行完populateBean方法之后,beanB已经初始化完成,并且注入到beanA中

       

       但注意到此时exposedObject仍然是beanA的raw实例,并非前面创建的代理对象实例,而最终需要注入一级缓存是必须是代理对象实例,代码继续往下走读:

          

          doCreateBean方法的最后从缓存中获取beanA的实例,由于第二级缓存中包含了beanA的实例,故从第二级缓存中取出beanA的实例赋值给exposedObject,这里beanA实例是代理对象。

          最后beanA也存储到一级缓存中,至此beanA和beanB实例化完成。

          这里思考一个问题,如果只用二级缓存是否可以解决循环依赖问题,对spring的代码进行改造,在加入到三级缓存之后立即生成实例对象并放入到二级缓存中,相当于三级缓存失效

// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		if (logger.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
	    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

        getSingleton(beanName, true);
    }

       经过测试,spring启动速度并没有受到影响,那为什么spring中需要有三级缓存呢?

       这是由于如果存在循环依赖并且某个bean存在aop代理的情况,则必须要提前创建好该bean的代理对象,而spring无法提前知道是否存在循环依赖, 为了兼容这种情况,则必须对所有bean对象都提前创建好代理对象,而这违背了spring bean的创建原则。spring是在bean创建完成之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器完成,其中的postProcessAfterInstantiation方法对初始化完成的bean完成AOP代理。我们来看下一个没有循环依赖但开启了aop代理的bean初始化情况,这里去除beanB中的beanA依赖

          这里虽然将工厂对象加入到三级缓存中,但是后面并未使用到该工厂对象

          

          执行完populateBean之后,beanA中的beanB初始化完成,此时beanA是原始对象

           执行完initializeBean方法之后,exposedObject赋值为代理对象

             因此spring使用第三级缓存的目的在于处理循环依赖中aop对象情景,工厂对象起到了延迟加载代理对象的作用:若只有aop没有循环依赖,则第三级缓存不会起作用,在bean创建完成之后再进行代理对象的创建;如果即有aop又有循环依赖,则通过工厂对象获取代理对象再加入到第二级缓存中

           

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值