Spring三级缓存与依赖循环
前言
假设有两个类A和B,他们互相引用对方作为属性,那么实例化这两个类的时候就会产生依赖循环问题。Spring使用三级缓存解决了bean之间的依赖循环问题,前提是这些bean使用的是默认的单例模式。以下内容基于Spring源码分析。
三级缓存
Spring使用三级缓存保存不同时期的bean,三级缓存的位置在DefaultSingletonBeanRegistry这个类三种,三级缓存其实就是三个Map对象:
- singletonObjects:一级缓存,存放完全实例化的bean对象。
- earlySingletonObjects:二级缓存,存放的是已经实例化但是还没有填充属性的bean对象
- singletonFactories:存放的是bean的工厂,可以从三级缓存获取bean的工厂对象,然后通过通常对象去实例化bean。
准备测试用例
版本信息
JDK:21
Spring:6.0.12
依赖
为了实现最小化的使用Spring容器的功能,之引入以下的依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> </dependencies>AB测试类
准备AB两个测试类,让他们互相引用对方作为属性
public class A { private B b; public A() { System.out.println("a的无参构造调用"); } public B getB() { return b; } public void setB(B b) { System.out.println("Aset注入B"); this.b = b; } @Override public String toString() { return "A{" + "b=" + b.hashCode() + '}'; } } public class B { private A a; public B() { System.out.println("b的无参构造调用"); } public A getA() { return a; } public void setA(A a) { System.out.println("Bset注入a"); this.a = a; } @Override public String toString() { return "B{" + "a=" + a.hashCode() + '}'; } }Bean配置文件
由于使用的是最小化的Spring功能,所以没有引入注解功能,所以使用xml编写bean配置文件实例化bean。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="a" class="com.dyl.spring6.A"> <property name="b" ref="b"/> </bean> <bean id="b" class="com.dyl.spring6.B"> <property name="a" ref="a"/> </bean> </beans>测试类
使用ClassPathXmlApplicationContext加载xml配置文件,实现bean的注入。
@Test public void test1() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-bean.xml"); A a = context.getBean("a", A.class); B b = context.getBean("b", B.class); System.out.println(a); System.out.println(b); }源码分析
ClassPathXmlApplicationContext
从ClassPathXmlApplicationContext的构造方法进入,里面调用了refresh()方法,这个是实例化的入口
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); this.setConfigLocations(configLocations); if (refresh) { // 核心代码是这一个 this.refresh(); } }进入到refresh()方法当中,发现这个方法调用了一个finishBeanFactoryInitialization()方法,这个方法对传入的bean工厂做了一些处理
this.finishBeanFactoryInitialization(beanFactory);
进入到finishBeanFactoryInitialization()方法当中,里面调用了bean工厂的preInstantiateSingletons()方法,这个方法会去实例化单例对象,即Spring当中使用工厂模式去创建bean
beanFactory.preInstantiateSingletons()
preInstantiateSingletons
进入到preInstantiateSingletons()方法内部,这个方法开始执行具体的实例化过程,这里循环bean名称,然后去实例化bean,中间有个if else判断,主要看单例bean的实例化,其他的非单例的不管。
public void preInstantiateSingletons() throws BeansException { if (this.logger.isTraceEnabled()) { this.logger.trace("Pre-instantiating singletons in " + this); } List<String> beanNames = new ArrayList(this.beanDefinitionNames); for(String beanName : beanNames) { RootBeanDefinition bd = this.getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (this.isFactoryBean(beanName)) { Object bean = this.getBean("&" + beanName); if (bean instanceof SmartFactoryBean) { SmartFactoryBean<?> smartFactoryBean = (SmartFactoryBean)bean; if (smartFactoryBean.isEagerInit()) { this.getBean(beanName); } } } else { // 获取单例bean this.getBean(beanName); } } } for(String beanName : beanNames) { Object singletonInstance = this.getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) { StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize").tag("beanName", beanName); smartSingleton.afterSingletonsInstantiated(); smartInitialize.end(); } } }getBean&doGetBean
进入getBean方法获取bean,方法实际上又调用了doGetBean方法。
doGetBean方法的核心功能有:
- 从缓存当中获取bean
- 如果缓存当中没有的话就创建bean并放到缓存当中
以上两个核心功能其实都调用了getSingleton方法——获取单例对象,区别在于调用的getSingleton是经过重载之后的两个不同的方法,里面逻辑不同。
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = this.transformedBeanName(name); // 从缓存获取bean Object sharedInstance = this.getSingleton(beanName); Object beanInstance; if (sharedInstance != null && args == null) { if (this.logger.isTraceEnabled()) { if (this.isSingletonCurrentlyInCreation(beanName)) { this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); } } beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null); } else { if (this.isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } BeanFactory parentBeanFactory = this.getParentBeanFactory(); if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) { String nameToLookup = this.originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { AbstractBeanFactory abf = (AbstractBeanFactory)parentBeanFactory; return (T)abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } if (args != null) { return (T)parentBeanFactory.getBean(nameToLookup, args); } if (requiredType != null) { return (T)parentBeanFactory.getBean(nameToLookup, requiredType); } return (T)parentBeanFactory.getBean(nameToLookup); } if (!typeCheckOnly) { this.markBeanAsCreated(beanName); } StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate").tag("beanName", name); try { if (requiredType != null) { Objects.requireNonNull(requiredType); beanCreation.tag("beanType", requiredType::toString); } RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName); this.checkMergedBeanDefinition(mbd, beanName, args); String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for(String dep : dependsOn) { if (this.isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } this.registerDependentBean(dep, beanName); try { this.getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } if (mbd.isSingleton()) { // 创建bean,并加入缓存,最后返回bean sharedInstance = this.getSingleton(beanName, () -> { try { return this.createBean(beanName, mbd, args); } catch (BeansException ex) { this.destroySingleton(beanName); throw ex; } }); beanInstance = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { Object prototypeInstance = null; try { this.beforePrototypeCreation(beanName); prototypeInstance = this.createBean(beanName, mbd, args); } finally { this.afterPrototypeCreation(beanName); } beanInstance = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); if (!StringUtils.hasLength(scopeName)) { throw new IllegalStateException("No scope name defined for bean '" + beanName + "'"); } Scope scope = (Scope)this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { this.beforePrototypeCreation(beanName); Object var4; try { var4 = this.createBean(beanName, mbd, args); } finally { this.afterPrototypeCreation(beanName); } return var4; }); beanInstance = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new ScopeNotActiveException(beanName, scopeName, ex); } } } catch (BeansException ex) { beanCreation.tag("exception", ex.getClass().toString()); beanCreation.tag("message", String.valueOf(ex.getMessage())); this.cleanupAfterBeanCreationFailure(beanName); throw ex; } finally { beanCreation.end(); } } return (T)this.adaptBeanInstance(name, beanInstance, requiredType); }getSingleton1
先看一下第一个获取单例对象的方法,使用到了synchronized同步锁,代码核心逻辑就是按照顺序依次从123级缓存获取bean,那么就可以分为以下几种情况:
- 对于刚初始化的Spring容器,三级缓存肯定是都不存在bean的,所以这个方法无法获取到bean
- 对于首次实例化且存在依赖循环的情况的bean,会放到第三级缓存当中——后续会看到。第三级缓存存放的是bean的工厂对象,然后通过工厂对象去获取bean实例——工厂模式。从第三级缓存获取bean之后,会移除掉原第三级缓存的beanFactory,然后将bean放到二级缓存当中。
- 对于上面第二种情况的bean,在二级缓存当中并不是完全体的bean,如果此时再次调用getSingleton方法,那么就会直接从二级缓存当中获取
- 对于已经完全实例化的bean,会放到第一级缓存,getSingleton方法直接从第一级缓存的当中返回
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从一级缓存获取bean Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { // 从二级缓存获取bean singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { synchronized(this.singletonObjects) { // 从一级缓存获取bean singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { // 从二级缓存获取bean singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { // 从三级缓存获取bean工厂 ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); if (singletonFactory != null) { // bean工厂获取bean singletonObject = singletonFactory.getObject(); // bean从三级缓存移除,移入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }getSingleton2
对于beanA和beanB,第一次获取getBean的时候,肯定是不在任意一级缓存当中的,所以接下来看第二个getSingleton方法。这个方法比第一个getSingleton方法多了一个函数式接口传参,这个传参用于传入一个获取bean实例的方法,这里传递的是createBean方法。这第二个getSingleton方法的核心逻辑是:
- 尝试从一级缓存获取bean
- 一级缓存获取不到就调用外部传入的创建bean实例的方法,注意这里创建bean实例的方法返回的是一个完全实例化的bean,这个后续会看到
- 将完全实例化的bean放到一级缓存当中,即调用了addSingleton方法。进入addSingleton方法可以看到,逻辑很简单,即将bean放到一级缓存当中,然后将二级缓存和三级缓存对应的bean都移除掉。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized(this.singletonObjects) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (this.logger.isDebugEnabled()) { this.logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } this.beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = this.suppressedExceptions == null; if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet(); } try { // 创建bean singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException var17) { BeanCreationException ex = var17; if (recordSuppressedExceptions) { for(Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } this.afterSingletonCreation(beanName); } if (newSingleton) { // 将bean加入到一级缓存 this.addSingleton(beanName, singletonObject); } } return singletonObject; } }createBean&doCreateBean
接下来看第二个获取单例对象getSingleton方法调用的创建对象createbean方法,这个方法会调用doCreateBean去创建bean,创建的是完全实例化的bean即已经实例化并填充好属性的bean。
看一下doCreateBean方法的核心逻辑:
- 判断是否是早期曝光对象,这个判断逻辑与bean的模式有关,必要条件之一就是bean需要处于单例模式,所以这也是为什么只有单例模式才能解决循环依赖的原因。
- 将bean的工厂放到第三级缓存当中 ,放的是bean的工厂此时并没有实例化bean,可以看到代码当中传入的是一个函数式的接口,只有在用到的时候才会调用。
- 填充bean的属性,这里会从Spring容器当中找到bean属性对应的值,如果没有找到的话就会去实例化对应的属性。有了这个方法,doCreateBean方法最终才会获取到完全实例化的bean。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = this.createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } synchronized(mbd.postProcessingLock) { if (!mbd.postProcessed) { try { this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.markAsPostProcessed(); } } // 判断是否是早期曝光对象 boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName); if (earlySingletonExposure) { if (this.logger.isTraceEnabled()) { this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 将bean工厂放到第三级缓存当中 this.addSingletonFactory(beanName, () -> this.getEarlyBeanReference(beanName, mbd, bean)); } Object exposedObject = bean; try { // 填充bean的属性 this.populateBean(beanName, mbd, instanceWrapper); exposedObject = this.initializeBean(beanName, exposedObject, mbd); } catch (Throwable var18) { if (var18 instanceof BeanCreationException bce) { if (beanName.equals(bce.getBeanName())) { throw bce; } } throw new BeanCreationException(mbd.getResourceDescription(), beanName, var18.getMessage(), var18); } if (earlySingletonExposure) { Object earlySingletonReference = this.getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) { String[] dependentBeans = this.getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length); for(String dependentBean : dependentBeans) { if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } } try { this.registerDisposableBeanIfNecessary(beanName, bean, mbd); return exposedObject; } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } }populateBean
populateBean方法用于给实例化后的bean填充属性,假设在发生了依赖循环的情况下,一个最普通bean属性的填充链路如下:
- applyPropertyValues
- resolveValueIfNecessary
- resolveReference
- applyPropertyValues——调用set方法注入属性
链路当中最核心的是第三步,resolveReference,这个方法里面实际上调用了beanFactory.getBean方法,那么这个方法就又回到了getBean这一步骤——即如果属性没有实例化那就会去实例化属性的bean,如果实例化了那么就会从三个级别的缓存获取。
bean = this.beanFactory.getBean(resolvedName);
执行逻辑
假设AB两个对象互相依赖,且A先实例化。
1、A实例化获取A的bean,此时缓存当中没有beanA,将A工厂放到三级缓存当中(提前曝光)。
2、A继续填充属性B,获取beanB,此时缓存当中没有beanB,单例模式且循环依赖的情况下,将B的工厂放到三级缓存当中(提前曝光)。
3、B继续填充属性A,获取beanA,此时可以直接从三级缓存当中通过A的工厂获取A的实例,并且会将beanA放到二级缓存,同时删除三级缓存当中的A工厂。B通过set填充A之后就属于完全实例化的bean,然后将B放到一级缓存,并删除三级缓存的B。
4、前面B放到一级缓存当中了,A填充属性B,此时直接从一级缓存当中获取,通过set注入,A属于完全实例化的bean,放到一级缓存当中,删除二级缓存。
PS:A实例化的过程中,经过了三级、二级、一级缓存的过程,而B实例化的时候没有经过二级缓存
