2020年8月11日 浙江大华面试原题
你看过spring源码吗,spring是怎么解决循环依赖的?
出现spring依赖的场景
import org.springframework.stereotype.Service;/** * Created by wt on 2020/9/12. * * @author wt */@Servicepublic class BeanA { private BeanB b; @Autowired //注意是构造器注入 public BeanA(BeanB b){ this.b=b; }}
package com.wtxm.springboot.beans;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;/** * Created by wt on 2020/9/12. * * @author wt */@Servicepublic class BeanB { private BeanA a; @Autowired public BeanB(BeanA a) { this.a = a; }}
如下图,BeanA和BeanB 互相依赖
启动项目
***************************APPLICATION FAILED TO START***************************Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐| beanA defined in file [D:\code\2020\springboot\target\classes\com\wtxm\springboot\beans\BeanA.class]↑ ↓| beanB defined in file [D:\code\2020\springboot\target\classes\com\wtxm\springboot\beans\BeanB.class]└─────┘Disconnected from the target VM, address: '127.0.0.1:61392', transport: 'socket'Process finished with exit code 1
这就是所谓的循环依赖问题
2.怎么解决循环依赖
使用单例bean,使用属性注入的方式,不采用构造器注入方式
3.Spring 解决循环依赖的原理
1.基础知识
对象实例化过程=当前对象实例化和对象属性的实例化
2.Spring 创建Bean的步骤
①createBeanInstance:初始化对象,类似于clazz.newInstance()
②populateBean:填充属性,bean的对象依赖属性就是在这一步进行填充
③initializeBean:调用spring配置中的init-method方法
经过上述三步,一个spring的bean才被完全创建。
4.源码分析:跟着方法调用链一步一步看源码
AbstractApplicationContext 的refersh()方法 是spring容器启动的重要方法,也是我们分析bean注入的入口
public void refresh() throws BeansException, IllegalStateException { synchronized(this.startupShutdownMonitor) { this.prepareRefresh(); ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); this.prepareBeanFactory(beanFactory); try { this.postProcessBeanFactory(beanFactory); this.invokeBeanFactoryPostProcessors(beanFactory); this.registerBeanPostProcessors(beanFactory); this.initMessageSource(); this.initApplicationEventMulticaster(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }
我们关注 finishBeanFactoryInitialization(beanFactory) 这个方法
此方法的作用是完成bean工厂的实例化工作
这个方法最后一行是核心代码beanFactory.preInstantiateSingletons();
进入DefaultListableBeanFactory这个类的preInstantiateSingletons()方法 。顾名思义,这个方法是初始化单例bean ,打断点调试,发现了我们的目标bean对象
找到填充bean 的核心方法
protected T doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { String beanName = this.transformedBeanName(name); //非常重要的方法 会详细解释 Object sharedInstance = this.getSingleton(beanName); Object bean; //第一次创建beanA 跳过此个if 因为sharedIntance是Null 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 + "'"); } } bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition) null); } else { if (this.isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } BeanFactory parentBeanFactory = this.getParentBeanFactory(); //第一次创建beanA对象 会跳过此次if parentBeanFactory为null if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) { String nameToLookup = this.originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } if (args != null) { return parentBeanFactory.getBean(nameToLookup, args); } if (requiredType != null) { return parentBeanFactory.getBean(nameToLookup, requiredType); } return parentBeanFactory.getBean(nameToLookup); } if (!typeCheckOnly) { this.markBeanAsCreated(beanName); } try { RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName); this.checkMergedBeanDefinition(mbd, beanName, args); String[] dependsOn = mbd.getDependsOn(); String[] var11; //第一次创建beanA对象 dependsOn为null if (dependsOn != null) { var11 = dependsOn; int var12 = dependsOn.length; for (int var13 = 0; var13 < var12; ++var13) { String dep = var11[var13]; 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 var24) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24); } } } if (mbd.isSingleton()) { //第一次创建beanA对象来到这 //这是 第二个需要重点关注的地方 会重点解释 sharedInstance = this.getSingleton(beanName, () -> { try { return this.createBean(beanName, mbd, args); } catch (BeansException var5) { this.destroySingleton(beanName); throw var5; } }); bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { var11 = null; Object prototypeInstance; try { this.beforePrototypeCreation(beanName); prototypeInstance = this.createBean(beanName, mbd, args); } finally { this.afterPrototypeCreation(beanName); } bean = 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; }); bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException var23) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var23); } } } catch (BeansException var26) { this.cleanupAfterBeanCreationFailure(beanName); throw var26; } } if (requiredType != null && !requiredType.isInstance(bean)) { try { T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } else { return convertedBean; } } catch (TypeMismatchException var25) { if (this.logger.isTraceEnabled()) { this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } else { return bean; } }
这个方法非常长,我们只关注几个非常重要的地方
第一个关注的地方是getSingleton()
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory> singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
singletonObjects 是单例对象集合,我们称之为一级缓存。因为我们是第一次创建BeanA对象,并且此对象是最初创建,故返回null (截图也可以看出来) 。isSingletonCurrentlyInCreation 这个属性表示当前Bean对象是否是正在创建,只有往singletonsCurrentlyInCreation这个集合里添加了Bean,才能表示当前Bean正在创建。我们第一次创建BeanA对象并没有设置,故这个循环体我们进不去,但是第二次创建BeanA对象的时候,我们会进入这个循环体,下面会具体分析。
代码中标注 的第二个需要关注的地方 getSingleton()
public Object getSingleton(String beanName, ObjectFactory> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized(this.singletonObjects) { //从一级缓存中取beanA对象,此时肯定取不到 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 { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException var16) { singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { throw var16; } } catch (BeanCreationException var17) { BeanCreationException ex = var17; if (recordSuppressedExceptions) { Iterator var8 = this.suppressedExceptions.iterator(); while(var8.hasNext()) { Exception suppressedException = (Exception)var8.next(); ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } this.afterSingletonCreation(beanName); } if (newSingleton) { this.addSingleton(beanName, singletonObject); } } return singletonObject; } }
singletonObjects这个集合 是存放单例对象的,显然此时一级缓存singletonObject还是为null,再看到beforeSingletonCreation()这个方法
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
打断点发现 inCreationCheckExclusions 为空,且做了一个非常重要的动作,就是往singletonsCurrentlyInCreation这个集合加了BeanA对象,这就是我们第二次创建BeanA对象进入不同的循环体的重要标识。退出getSingleton()方法,我们发现,传入该方法的参数是一个匿名对象,且createBean()实现了其接口的唯一方法T getObject() throws BeansException;
if(mbd.isSingleton()){ sharedInstance = this.getSingleton(beanName, () -> { try { return this.createBean(beanName, mbd, args); } catch (BeansException var5) { this.destroySingleton(beanName); throw var5; } }); bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
这里creatBean是真正创建Bean的地方 核心方法是 doCreateBean
此时循环判断条件是当前Bean对象是单例,允许循环引用,且有正在被创建的属性,前面已经设置过,故earlySingletonExposure为true
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); } } }
singletonFatories是三级缓存,添加了创建BeanA对象的BeanFactory;
earlySingletonObjects是二级缓存,清除了二级缓存中BeanA对象;
至此beanA对象初始化完毕,接着执行populateBean这个操作,发现BeanA有一个属性对象是BeanB类型。于是我们去初始化BeanB 类型的对象b,BeanB初始化的流程和beanA一模一样。
当BeanB 类型的Bean初始化完成之后,发现其有一个属性对象类型是BeanA,
接着spring就会继续去创建BeanA对象。但是这一次创建就和第一次创建beanA对象不一样了,我们接着来看那个超长的创建bean的方法
前面提到第一个需要关注的点
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //从一级缓存中取到 此时singletonObject非null Object singletonObject = this.singletonObjects.get(beanName); //此前beanA对象已经被设置为 CurrentlyInCreation 故进入到循环体 if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { synchronized(this.singletonObjects) { //从二级缓存中取,此前分析,二级缓存已经被清除,故进入循环体 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { //获取创建beanA对象的工厂 ObjectFactory> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); if (singletonFactory != null) { //获取工厂类 singletonObject = singletonFactory.getObject(); //存放二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); //清除三级缓存 this.singletonFactories.remove(beanName); } } } } // 返回的是二级缓存中的beanA 此时BeanB 对象初始化完毕 return singletonObject; }
附加解释:
AbstractAutowireCapableBeanFactory 类的 reateBeanInstance方法
这里可以开始解释为什么构造器注入不能解决循环依赖,可以自己点进去分析。
总结:
Spring第一次创建BeanA对象,在三级缓存中添加了创建BeanA对象的工厂,并删除了二级缓存中提前曝光的BeanA对象,初始化完毕之后,发现BeanA对象有一个BeanB类型的属性,接着去创建BeanB对象。BeanB对象的第一次创建过程和第一次创建BeanA对象一样。当初始化完BeanB对象后,发现其有一个属性是BeanA,故第二次去初始化实例化BeanA对象,此时创建BeanA的过程与第一次不同。因为第一次创建BeanA的时候,设置了BeanA对象为"正在创建",故此时能进入另一个处理逻辑------获取三级缓存的BeanFactory,通过这个来获取提前曝光的BeanA对象,接着删除二级缓存,这样拿到的BeanA对象虽然不完整,但足以依赖注入,BeanB对象也能完成实例化。然后BeanA对象也能顺利完成初始化,有种递归调用的感觉。