Spring三级缓存——解决循环依赖

330 阅读8分钟

对于循环依赖不熟悉的同学,可以看下我上一篇文章Spring循环依赖实践

什么是循环依赖

Bean之间互相依赖,并最终形成了闭环。

自我依赖

@Component
public class C {
    @Autowired
    private C c;
}

互相依赖

@Component
public class A {
    @Autowired
    private B b;
}

@Component
public class B {
    @Autowired
    private A a;
}

Spring三级缓存

三级缓存介绍

// DefaultSingletonBeanRegistry

	/** 用于存储已经初始化完毕的Bean */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** 用于存储Bean对应的ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** 用于存储已经实例化,属性未填充的Bean */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
缓存说明
singletonObjects第一级缓存,存放可用的成品Bean,即已经实例化,完成属性填充,且执行完初始化操作(init)。
earlySingletonObjects第二级缓存,存放半成品的Bean半成品的Bean是已创建对象,但是未注入属性和初始化。用以解决循环依赖。
singletonFactories第三级缓存,存的是Bean工厂对象,用来生成半成品的Bean并放入到二级缓存中。用以解决动态代理的循环依赖

解决循环依赖的过程

A -> B -> A
  1. 实例化A:因为缓存中不存在A,就实例化A,这时的A是半成品(未填充属性和init)
  2. 提前曝光A:将A放入第三级缓存(用ObjectFactory包装)
  3. 填充属性到A:
  4. 实例化B:因为缓存中不存在B,就实例化B,这时的B是半成品(未填充属性和init)
  5. 提前曝光B:将B放入第三级缓存(用ObjectFactory包装)
  6. 填充属性到B:
  7. 从缓存获取实例A:通过第三级缓存,获取A实例(A这里还是半成品),并放入第二级缓存
  8. B完成属性填充、init,加入第一级缓存
  9. A完成属性填充、init,加入第一级缓存

源码分析 - 获取Bean

接下来开始Spring解决循环依赖的源码分析。Spring创建Bean,解决循环依赖的代码主要分布在:

  • DefaultSingletonBeanRegistry
  • AbstractBeanFactory
  • AbstractAutowireCapableBeanFactory

Spring Bean生命周期的入口AbstractBeanFactory#doGetBean

// AbstractBeanFactory

	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		// 1.从三级缓存的Map中获取数据
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			//......
		}

		else {
			//......

			try {
				//Bean不在缓存中

				// 创建单例的Bean实例
				if (mbd.isSingleton()) {
                                   //2.创建单例Bean
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							//......
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// 创建prototype的Bean实例
					......
				}

				else {
					//其他scope的处理逻辑
                    ......
				}
			}
			......
		}

		// 返回Bean
		return (T) bean;
	}
  1. 从三级缓存中获取Bean,成功获取Bean,直接返回

  2. 缓存中获取不到Bean,创建单例Bean,返回Bean。这块的逻辑由两部分组成:

    2.1 getSingleton(String beanName, ObjectFactory<?> singletonFactory)

    2.2 执行createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)创建Bean

1. 从三级缓存获取Bean - getSingleton(String beanName, boolean allowEarlyReference)

// DefaultSingletonBeanRegistry

	/**
	 * @param allowEarlyReference 是否使用第三级缓存开关
	 */
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 1.从singletonObjects获取Bean
		Object singletonObject = this.singletonObjects.get(beanName);
        //Bean未初始化完成,且Bean还在创建中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
                // 2. 从earlySingletonObjects中获取Bean
				singletonObject = this.earlySingletonObjects.get(beanName);
                // 3.未获取到Bean,且allowEarlyReference == true
				if (singletonObject == null && allowEarlyReference) {
                    // 从singletonFactories获取Bean
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
                        // 获取Bean
						singletonObject = singletonFactory.getObject();
                        // 将Bean放置到earlySingletonObjects
						this.earlySingletonObjects.put(beanName, singletonObject);
                        // 从singletonFactories中移除Bean
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
  1. 从第一级缓存(singletonObjects)获取Bean的完成品
  2. 第一级缓存未获取到Bean,从第二级缓存(earlySingletonObjects)获取半成品
  3. 第二级缓存未获取到Bean,从第三级缓存(singletonFactories)获取Bean,并放入第二级缓存

2. 从ObjectFactory中获取Bean - getSingleton(String beanName, ObjectFactory<?> singletonFactory)

// DefaultSingletonBeanRegistry

	public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		synchronized (this.singletonObjects) {
            //1. 从第一级缓存(singletonObjects)中取
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				......
				try {
                    // 2.未取到,调用ObjectFactory#getObject(),获取Bean实例
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				......
				if (newSingleton) {
                    // 将生成的Bean添加到第一级缓存中
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}
  1. 第一级缓存获取Bean,如果获取到了,直接返回
  2. 如果第一级缓存未获取到Bean,调用ObjectFactory#getObject(),获取Bean实例
  3. 将Bean加入第一级缓存

2.1 将Bean加入第一级缓存 - addSingleton()

// DefaultSingletonBeanRegistry

	// 将初始化完毕的Bean加入一级缓存(singletonObjects),并从三级缓存(singletonFactories)和二级缓存(earlySingletonObjects)中移除
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			this.singletonObjects.put(beanName, singletonObject);
			this.singletonFactories.remove(beanName);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}

2.2 创建Bean - createBean()

#getSingleton(String beanName, ObjectFactory<?> singletonFactory)内部singletonFactory.getObject()实际上调用#creatBean()创建Bean。

//AbstractAutowireCapableBeanFactory
	
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
        ......
        // 执行doCreateBean创建Bean
    	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    	......
    }
	

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		//1. 创建Bean实例
        BeanWrapper instanceWrapper = null;
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		//2. 判断是否需要早期暴露Bean,用于解决循环依赖
		boolean earlySingletonExposure = (mbd.isSingleton() // 是否是单例
                                          && this.allowCircularReferences // 是否允许循环依赖 
                                          && isSingletonCurrentlyInCreation(beanName)); // 当前Bean是否正在创建
		if (earlySingletonExposure) {
			//3. 将beanName对应的ObjectFactory存入singletonFactories,再次获取Bean时,直接从singletonFactories中获取
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
		Object exposedObject = bean;
		try {
            //4.填充属性
			populateBean(beanName, mbd, instanceWrapper);
			//5.执行init操作
            exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			//.......
		}
        
        // 6.从缓存获取提前暴露的对象
        if (earlySingletonExposure) {
            //如果Bean被AOP代理,这里就会从earlySingletonObjects中获取到代理对象
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				if (exposedObject == bean) {
                    // earlySingletonReference -> exposedObject
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                	// .....
                }
			}
		}

       //......

		return exposedObject;
	}
  1. 创建Bean

  2. 判断Bean是否需要提前暴露

  3. 对于需要提前暴露的Bean放入第三级缓存(singletonFactories

    3.1 执行#addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory)添加Bean到第三级缓存

    3.2 #getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean):这一步是解决动态代理对象循环依赖的关键

  4. 属性填充

  5. 执行init逻辑,执行完该逻辑后Bean就成为了完成品

  6. 从缓存中获取早期暴露的Bean

为什么要第三级缓存?

从提前曝光的流程看,Spring实际上仅需提供二级缓存即可解决循环依赖问题,即第一级缓存用于存储完成品,第二级缓存用于存储半成品。那为什么Spring需要提供第三级缓存?实际上Spring用第三级缓存解决Bean的动态代理问题

对于动态代理Bean的生成可以解决的方法应该有两种:

  1. 在实例化Bean之后,未填充属性和init之前,直接生成Bean的动态代理,这样实际上就不需要第三级缓存。但是,这种方式违背了Spring生命周期的设定,即在Bean生命周期的最后一步,才会生成Bean的代理
  2. Spring提供的方式:在实例化Bean之后,将Bean的半成品放入第三级缓存,并不会提前创建动态代理对象。在出现循环依赖时,生成Bean的代理对象

解决动态代理对象的循环依赖的过程

A(代理) -> B(代理) -> A(代理)
  1. 实例化A:因为缓存中不存在A,就实例化A,这时的A是半成品(未填充属性和init)
  2. 提前曝光A:将A放入第三级缓存(用ObjectFactory包装)
  3. 填充属性到A:
  4. 实例化B:因为缓存中不存在B,就实例化B,这时的B是半成品(未填充属性和init)
  5. 提前曝光B:将B放入第三级缓存(用ObjectFactory包装)
  6. 填充属性到B:
  7. 从缓存获取实例A:通过第三级缓存获取A的代理对象,并放入第二级缓存
  8. B完成属性填充、执行init:生成B的代理对象,这时B已经是完成品,将B代理对象加入第一级缓存
  9. A完成属性填充、执行init
  10. 从二级缓存获取A代理对象,代替原来的A对象,将A代理对象加入第一级缓存

存储Bean的ObjectFactory

Spring解决动态代理的循环依赖问题,主要涉及两步:

  • #创建Bean-步骤3 - addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));:将Bean封装成ObjectFactory,放入第三级缓存
  • 获取Bean-步骤1-getSingleton(beanName);:从第三级缓存中取出ObjectFactory,调用ObjectFactory#getObject(),实际执行#getEarlyBeanReference(beanName, mbd, bean)获取代理对象
// DefaultSingletonBeanRegistry

	// 将beanName和对应的singletonFactory存入singletonFactories
	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
            // 如果beanName在singletonObjects没有已经初始化完成的Bean,就保存singletonFactory到singletonFactories
			if (!this.singletonObjects.containsKey(beanName)) {
				this.singletonFactories.put(beanName, singletonFactory); //将singletonFactory存入第三级缓存
				this.earlySingletonObjects.remove(beanName); //从earlySingletonObjects中移除Bean
				this.registeredSingletons.add(beanName); //表示Bean已经注册
			}
		}
	}

产生动态代理对象

注意:

  • 在循环依赖场景,Spring通过#getEarlyBeanReference()产生动态代理。
  • 如果不存在循环依赖,Spring在#initializeBean(beanName, exposedObject, mbd)阶段生成动态代理对象
//AbstractAutowireCapableBeanFactory

	//用于解决循环依赖的场景,返回bean
	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        // 获取要返回的对象
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
                // AnnotationAwareAspectJAutoProxyCreator#getEarlyBeanReference会返回代理对象
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}
  • Spring通过AnnotationAwareAspectJAutoProxyCreator#getEarlyBeanReference会创建动态代理对象

产生代理类的时机

虽然,A和B都会产生动态代理对象,但是,执行的流程是不一样的。

  • A代理:B属性填充(#populateBean()) -> getBean(name) -> doGetBean(name,null,null,false) -> getSingleton(beanName) -> #getEarlyBeanReference() -> AnnotationAwareAspectJAutoProxyCreator#getEarlyBeanReference()
  • B代理:B初始化(#initializeBean()) -> AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization()

是否可以去掉三级缓存,变为二级?

可以,在实例化Bean之后,未填充属性和init之前,直接生成Bean的动态代理,这样实际上就不需要第三级缓存。

其它问题

能否解决scope=prototype的循环依赖?

Spring是通过缓存机制解决循环依赖,而scope=prototype的Bean不会被缓存,所以Spring无法解决prototype的Bean的循环依赖问题。

能否解决构造函数器注入的循环依赖?

Spring官方文档:

  • If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.

    For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.

    One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.

    Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).

官方文档的含义:

  1. 纯构造器注入的循环依赖无解
  2. Setter注入可以解决循环依赖问题

参考