基于Spring-5.1.5.RELEASE
都知道Spring
经过三级缓存
来解决循环依赖
的问题。可是是否是必须三级缓存
才能解决,二级缓存
不能解决吗?
要分析是否是能够去掉其中一级缓存,就先过一遍Spring
是如何经过三级缓存
来解决循环依赖
的。java
所谓的循环依赖
,就是两个或则两个以上的bean
互相依赖对方,最终造成闭环
。好比“A对象依赖B对象,而B对象也依赖A对象”,或者“A对象依赖B对象,B对象依赖C对象,C对象依赖A对象”;相似如下代码:面试
public class A { private B b; } public class B { private A a; }
常规状况下,会出现如下状况:缓存
Spring
解决循环依赖
的核心思想在于提早曝光
:app
半成品对象A
放入半成品缓存
。半成品缓存
里取到半成品对象A
。完成品B对象
放入完成品缓存
。完成品缓存
中取到完成品B对象
并注入。完成品A对象
放入完成品缓存
。其中缓存有三级:ide
/** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
缓存 | 说明 |
---|---|
singletonObjects | 第一级缓存,存放可用的成品Bean 。 |
earlySingletonObjects | 第二级缓存,存放半成品的Bean ,半成品的Bean 是已建立对象,可是未注入属性和初始化。用以解决循环依赖。 |
singletonFactories | 第三级缓存,存的是Bean工厂对象 ,用来生成半成品的Bean 并放入到二级缓存中。用以解决循环依赖。 |
要了解原理,最好的方法就是阅读源码,从建立Bean的方法AbstractAutowireCapableBeanFactor.doCreateBean
入手。函数
Bean
对象以后,将对象提早曝光
到缓存中,这时候曝光
的对象仅仅是构造完成
,还没注入属性
和初始化
。public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { …… // 是否提早曝光 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)); } …… } }
Map<String, ObjectFactory<?>> singletonFactories
缓存中,这里并非直接将Bean
放入缓存,而是包装成ObjectFactory
对象再放入。public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { 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); } } } } public interface ObjectFactory<T> { T getObject() throws BeansException; }
ObjectFactory
对象?若是建立的Bean
有对应的代理
,那其余对象注入时,注入的应该是对应的代理对象
;可是Spring
没法提早知道这个对象是否是有循环依赖
的状况,而正常状况
下(没有循环依赖
状况),Spring
都是在建立好完成品Bean
以后才建立对应的代理
。这时候Spring
有两个选择:post
循环依赖
,都提早
建立好代理对象
,并将代理对象
放入缓存,出现循环依赖
时,其余对象直接就能够取到代理对象并注入。循环依赖
被其余对象注入时,才实时生成代理对象
。这样在没有循环依赖
的状况下,Bean
就能够按着Spring设计原则
的步骤来建立。Spring
选择了第二种方式,那怎么作到提早曝光对象而又不生成代理呢?
Spring就是在对象外面包一层ObjectFactory
,提早曝光的是ObjectFactory
对象,在被注入时才在ObjectFactory.getObject
方式内实时生成代理对象,并将生成好的代理对象放入到第二级缓存Map<String, Object> earlySingletonObjects
。addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
:性能
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { 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; } }
为了防止对象在后面的初始化(init)
时重复代理
,在建立代理时,earlyProxyReferences
缓存会记录已代理的对象。测试
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { private final Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16); @Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); } }
提早曝光以后:this
populateBean
方法注入属性,在注入其余Bean
对象时,会先去缓存里取,若是缓存没有,就建立该对象并注入。initializeBean
方法初始化对象,包含建立代理。public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { …… // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } …… } } // 获取要注入的对象 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { 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; } }
在经历了如下步骤以后,最终经过addSingleton
方法将最终生成的可用的Bean
放入到单例缓存
里。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { /** Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); /** Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); 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); } } }
上面第三步《为何要包装一层ObjectFactory对象?》
里讲到有两种选择:
循环依赖
,都提早
建立好代理对象
,并将代理对象
放入缓存,出现循环依赖
时,其余对象直接就能够取到代理对象并注入。循环依赖
被其余对象注入时,才实时生成代理对象
。这样在没有循环依赖
的状况下,Bean
就能够按着Spring设计原则
的步骤来建立。Sping
选择了第二种
,若是是第一种
,就会有如下不一样的处理逻辑:
提早曝光半成品
时,直接执行getEarlyBeanReference
建立到代理,并放入到缓存earlySingletonObjects
中。ObjectFactory
来延迟
执行getEarlyBeanReference
,也就不须要singletonFactories
这一级缓存。这种处理方式可行吗?
这里作个试验,对AbstractAutowireCapableBeanFactory
作个小改造,在放入三级缓存
以后马上取出并放入二级缓存
,这样三级缓存
的做用就彻底被忽略掉,就至关于只有二级缓存
。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { …… // 是否提早曝光 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); } …… } }
测试结果是能够的,而且从源码上分析能够得出两种方式性能是同样的,并不会影响到Sping
启动速度。那为何Sping
不选择二级缓存
方式,而是要额外加一层缓存?
若是要使用二级缓存
解决循环依赖
,意味着Bean在构造
完后就建立代理对象
,这样违背了Spring设计原则
。Spring结合AOP跟Bean的生命周期,是在Bean建立彻底
以后经过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization
方法中对初始化后的Bean完成AOP代理。若是出现了循环依赖
,那没有办法,只有给Bean先建立代理,可是没有出现循环依赖的状况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。
参考:
《 面试官:聊聊Spring源码的生命周期、循环依赖》
《 面试必杀技,讲一讲Spring中的循环依赖》