前言
本章节开始前先回忆一下现在我们都做了哪些工作,首先定义几个beanFactoryPostProcessor类,执行了其中的方法,读取到了项目中符合条件的类,但是没有实例化及初始化,找到了一些实现了beanPostProcessor接口的类,但是还没有执行它的before和after方法,目前就做了这些工作。今天这节课要做的就是完成剩余的工作,对剩余的符合条件的bean完成实例化初始化的工作。说到这里就不得不提及两个概念了--“循环依赖”和“三级缓存”了。
对SpringBoot源码有所学习的朋友应该都会听过循环依赖这个名词,那么什么是循环依赖,简单的说,就是解决类的实例化过程的死循环问题,举个例子,我们有两个类,分别是A和B,A中包含了B属性,B中又包含了A属性,这样当我们想要对A和B实例化的时候,就会形成了死循环,大家都知道传参主要有两种方式,一种是构造函数传参,一种是调用set方法,我们先来看看构造函数传参的方式是否可以解决循环依赖。
先创建两个类,A和B,然后我们实例化A,看看效果:
public class A { public A(B b){ this.b = b; } private B b; public B getB() { return b; } public void setB(B b) { this.b = b; } }
public class B { public B(A a){ this.a = a; } private A a; public A getA() { return a; } public void setA(A a) { this.a = a; } }
我们写一个测试方法,实例化A,调用A的构造函数,看看效果:
public class test { public static void main(String[] args) { A a = new A(new B());//这里编译报错 } }
实例化A的时候,需要传递一个B的实例作为参数,但是创建B实例的时候又需要A的实例作为参数,这就进入了一个死循环的状态,永远也无法创建成功。刚刚说了,传参有两种方式,现在看来构造函数的方式是行不通了,那么就试试调用set方法的方式,这就引出了我们今天要讲的,解决循环依赖的问题了。
循环依赖的解决方案
循环依赖产生的原因是就是两个类之间相互调用导致的,创建A对象时用到了B实例,创建B对象时又用到了A实例:

图1
创建A实例的时候需要填充A中的B属性,此时用到了B对象,所以需要创建B实例,但是填充B中的A属性时有需要创建A对象,从而陷入了死循环,那么怎么解决呢?
我们创建A对象,实例化A的时候,如果我们把这个实例化完成的A放入到一个容器里面,等到创建B对象、填充B中的A属性时,直接从容器中拿到这个A实例是不是就可以了,当然,此时的A是一个半成品,是一个还没有填充属性的A,但是我们既然拿到了这个A实例了,持有了这个半成品的引用,后面再为他填充属性是不是也可以了。下面看看我画的这张图

图2
三级缓存
上图中已经看到了缓存这个概念,上图的缓存装载了半成品的A对象。下面为了更好的阅读代码,我先说一下一二三级缓存都存储了什么内容,三级缓存在DefaultSingletonBeanRegistry类里面,我们看看这三个集合的定义:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { /** * 下面这三个集合就是对应的一二三级缓存,都是key-value形式的 * 一级缓存装载了成品 * 二级缓存装载的是半成品 * 三级缓存略有特殊,它的value是一个ObjectFactory,我们点进去看看,这个ObjectFactory是一个函数式接口,包含了一个getObject方法 * */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); private final Map<String, Object> earlySingletonObjects = new HashMap(16); private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); }
有的同学会发出疑问,为什么会有一级缓存和二级缓存的区别,成品和半成品都放入一个集合行不行?肯定是不行的,如果都放入一个集合,那我们怎么区分成品和半成品呢,比如说实例化a的时候,需要对b属性赋值,那你从集合拿出来的怎么确保最终是一个成品的b呢?所以需要分开存储。那三级缓存的作用是什么呢,为什么它的值是一个函数式接口呢,带着这个问题,我们继续往下看。
源码跟踪
这个系列是按照SpringBoot的源码执行顺序讲解的,所以今天将讲解的就是refresh中的finishBeanFactoryInitialization方法了,这个方法执行之后将会实例化了剩余的所有非懒加载的bean。这个方法前面都是一些设置信息,不算重要,重要的代码行是这个方法的最后一行,也就是beanFactory.preInstantiateSingletons(),我们直接到这个方法中瞧瞧吧。
接下来这个方法要完成的就是bean的实例化初始化的工作,代码嵌套的层级比较多,为了便于大家的阅读,我先画出重点方法调用的过程图,大家阅读代码的时候重点关注这个方法调用链:

图3
下面我们接着以a和b两个类为实例(a和b自己创建就好,不要构造函数注入,注解的方式注入),跟踪代码,老规矩,粘贴重要的代码。
public void preInstantiateSingletons() throws BeansException { if (this.logger.isDebugEnabled()) { this.logger.debug("Pre-instantiating singletons in " + this); } List<String> beanNames = new ArrayList(this.beanDefinitionNames); for (String beanName : beanNames){ //合并父类BeanDefinition RootBeanDefinition bd = this.getMergedLocalBeanDefinition(beanName); //判断是否抽象、单例、懒加载 if(bd.isAbstract()&&bd.isSingleton()&&!bd.isLazyInit()){ //判断是否实现了FactoryBean接口 if (this.isFactoryBean(beanName)) { //如果实现了接口,则在beanName前面加上& Object bean = this.getBean("&" + beanName); if(isFactoryBean(beanName)){ ... ... } }else{ //如果不是FactoryBean,而是普通的bean,则通过beanName获取到bean实例 //《代码片段 doGetBean》 详细讲解 getBean(beanName); } } } }
代码片段 doGetBean
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { //提取对应的beanName,为什么不直接使用name呢,还要提取一次? //因为如果是FactoryBean的话,name的前面被加了一个& String beanName = this.transformedBeanName(name); //从缓存中查找bean,查找不到的话返回null 《代码片段 getSingleton》详细说明 Object sharedInstance = this.getSingleton(beanName); Object bean; //... ... //省略代码,更多的是处理特殊情况,如FactoryBean等的 try { RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName); if (mbd.isSingleton()) { //这里是重点,这里再次调用了getSingleton同名方法,传递参数不同,两个参数,第一个参数是beanName,第二个参数是一个函数式接口 //第二个参数是Lambda表达式,不熟悉的同学自行补课 //《代码片段getSingleton-2》、《代码片段doCreateBean》两个章节详细说明 sharedInstance = this.getSingleton(beanName, () -> { try { return this.createBean(beanName, mbd, args); } catch (BeansException var5) { //销毁bean 并抛出异常 this.destroySingleton(beanName); throw var5; } }); bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } }catch (Exception ex) {} return null; }
代码片段 getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) { //从一级缓存查找 Object singletonObject = this.singletonObjects.get(beanName); //如果已经缓存中没有,并且是正在创建的,则进入这个if if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { synchronized(this.singletonObjects) { //从二级缓存中查找 singletonObject = this.earlySingletonObjects.get(beanName); //二级缓存没找到,allowEarlyReference是入参,传入的就是true if (singletonObject == null && allowEarlyReference) { //三级缓存查找 ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); //三级缓存找到的话 if (singletonFactory != null) { //执行三级缓存存储的Lambda方法 singletonObject = singletonFactory.getObject(); //加入二级缓存(为什么装载入二级缓存,因为此时还是半成品) this.earlySingletonObjects.put(beanName, singletonObject); //从三级缓存移除 this.singletonFactories.remove(beanName); } } } } //如果从一二三级缓存找到的话则返回,否则返回空 return singletonObject; }
这段代码就是获取实例的方法,但是没涉及到创建的过程,我们以a为例,首次进入的时候a肯定是空的,而且一二三级缓存中都不存在,也不是正在创建的,所以首次进入会直接返回null。
仔细阅读这段代码,这段代码涉及到了三个if,进入第一个if的条件时一级缓存为空,并且正在创建,那什么是正在创建呢。正在创建的这个判断方法其实就是从singletonsCurrentlyInCreation集合中是否包含这个beanName,而这个集合的复制是在后面的方法中完成的,回到doGetBean方法,继续阅读,后面会讲到这个集合。第二个if的条件是二级缓存为空。第三个if的条件是三级缓存不为空,然后执行三级缓存的getObject方法,所以下面的学习还需要重点关注下三级缓存如何赋值的。
(加一句,我们首次进入的a,会返回null,那我们接下来的重点是什么?既然没从缓存找到,那当然是创建a的实例了,回头看下我们的图3,doGetBean下一个重要方法是什么,是createBean,接下来重点关注的就是这个方法了)
代码片段 getSingleton-2
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) { //如果当前在destorySingletons中,则抛出异常(正在销毁的bean) 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 + "'"); } //加入到正在创建的集合中,在《代码片段 getSingleton》中提到了,第一个if的判断的条件是正在创建中, // 这个正在创建的集合(singletonsCurrentlyInCreation)就是在这里添加的 this.beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = this.suppressedExceptions == null; if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet(); } try { //执行上面传进来的接口方法,就是上面提到的Lambda表达式,我们传进来的表达式调用了createBean方法,所以将会执行该方法 singletonObject = singletonFactory.getObject();
//从这里往下的代码,都会在执行createBean方法之后再继续执行,不懂的同学自行补习Lambda //标记生成了新对象,下面代码有用到这个变量 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; } //创建完成,从正在创建集合(singletonsCurrentlyInCreation)移除 this.afterSingletonCreation(beanName); } /** * 如果创建完成,则添加到缓存 * addSingleton方法不展开讲,代码很简单, * //加入一级缓存 * this.singletonObjects.put(beanName, singletonObject); * //从三级缓存移除 * this.singletonFactories.remove(beanName); * //从二级缓存移除 * this.earlySingletonObjects.remove(beanName); * //加入到已注册的单例集合中 * this.registeredSingletons.add(beanName); * */ if (newSingleton) { this.addSingleton(beanName, singletonObject); } } return singletonObject; } }
这段代码中有几个重点的地方,我都用红色字体做了标记,首先是把正在创建的单例对象添加到正在创建集合(SingletonCurrentlyInCreation),我们在《代码片段 getSingleton》中提到过这个集合,但是当时并不知道这个集合何时赋值的,现在对这个赋值问题进行了解答;第二个是执行Lambda表达式,这个是正式创建bean的单例对象;第三个红色标记的是创建完成后,从正在创建集合(SingletonCurrentlyInCreation)移除正在创建的bean。
代码片段 doCreateBean
我们在图3中已经知道了创建单例实例的方法调用链,createBean本身做的事情并不算多,这里就不详细讲解了,我们之间进入到doCreateBean方法学习。
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) { //创建实例,这个方法非常复杂,总体来说,需要知道经过这个方法之后,我们的bean完成了实例化,但是属性值都还是空 instanceWrapper = this.createBeanInstance(beanName, mbd, args); } //省略代码 ... ... synchronized(mbd.postProcessingLock) { if (!mbd.postProcessed) { try { //重要扩展点,执行MergedBeanDefinitionPostProcessor接口的postProcessMergedBeanDefinition //我们可以自定义类实现这个接口,在此时会被执行 this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable var17) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17); } mbd.postProcessed = true; //省略代码 ... ... boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName); if (earlySingletonExposure) { if (this.logger.isDebugEnabled()) { this.logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } /** * 先看下这个addSingletonFactory方法代码: * //如果beanName不存在于一级缓存 * if (!this.singletonObjects.containsKey(beanName)) { * //向三级缓存中加入这个beanName(value是什么?就是我们的()->那段方法) * this.singletonFactories.put(beanName, singletonFactory); * //从二级缓存中移除 * this.earlySingletonObjects.remove(beanName); * //添加到注册了的集合中 * this.registeredSingletons.add(beanName); * } * 这里第一次出现了写入三级缓存的操作,写入的是一个 * () -> { * return this.getEarlyBeanReference(beanName, mbd, bean); * } * 这个方法的作用先不用管 * 注意:执行了这段代码之后,我们的三级缓存中已经存储了我们正在创建的实例,value是一个Lambda表达式 * */ this.addSingletonFactory(beanName, () -> { return this.getEarlyBeanReference(beanName, mbd, bean); }); } Object exposedObject = bean; try { //对bean进行属性填充,将各个属性值注入,可能存在依赖的bean属性,所以会递归初始化依赖的bean //《代码片段 populateBean》详细说明 this.populateBean(beanName, mbd, instanceWrapper); //执行了类的注册时的init方法,并在执行方法前后执行我们的beanPostProcessor接口方法 //《代码片段 initializeBean》章节详细说明 exposedObject = this.initializeBean(beanName, exposedObject, mbd); } catch (Throwable var18) { if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) { throw (BeanCreationException)var18; } throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18); } } } }
代码片段 populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { boolean continueWithPropertyPopulation = true; //存在实现了InstantiationAwareBeanPostProcessors接口的类 if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) { Iterator var5 = this.getBeanPostProcessors().iterator(); while(var5.hasNext()) { BeanPostProcessor bp = (BeanPostProcessor)var5.next(); if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp; /** * 执行InstantiationAwareBeanPostProcessors的postProcessAfterInstantiation方法 * InstantiationAwareBeanPostProcessor还包含了postProcessBeforeInstantiation方法,这个方法是在createBean进入到doCreateBean之前执行的 */ if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } Iterator var9 = this.getBeanPostProcessors().iterator(); //省略代码... ... while(var9.hasNext()) { BeanPostProcessor bp = (BeanPostProcessor)var9.next(); if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp; /** * 执行InstantiationAwareBeanPostProcessors的postProcessPropertyValues方法 * 这个接口比较重要,里面有一个实现类 -- AutowiredAnnotationBeanPostProcessor * 在这个实现类的postProcessPropertyValues方法里面完成了依赖注入并解决了循环依赖问题 * 《代码片段 AutowiredAnnotationBeanPostProcessor-postProcessPropertyValues》详细说明 */ pvs = ibp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } if (pvs != null) { //Spring进来时完成依赖注入并解决循环依赖问题的方法 这里不做重点讲解,大概的流程和utowiredAnnotationBeanPostProcessor-postProcessPropertyValues类似 this.applyPropertyValues(beanName, mbd, bw, (PropertyValues)pvs); } } }
代码片段 AutowiredAnnotationBeanPostProcessor-postProcessPropertyValues
这个方法开始,调用的层级比较多,这里就不逐层列出了,我先绘制一个方法调用的流程图:

看看这个图,从A类取到了属性B,然后经过这个方法链的调用,最后又回到了getBean方法,可以看出,创建类实例时,如果这个类里面包含了其他类属性,就会先尝试创建这个属性的类的实例。现在创建B,和A的流程是一样的,接下来创建B的时候发现包含了A属性,又会回到getBean当中,那么重点来了:
1 再次执行getSingleton方法,判断A是否存在一级缓存中,发现不存在,然后继续判断二级缓存,如果二级缓存为空并且是正在创建的,则从三级缓存取出A,执行三级缓存中存入的Lambda表达式,并移入二级缓存;
2 把二级缓存中的A填充到B的a属性上;
3 把B装入一级缓存,并把B从二级、三级缓存移除;
4 把完成的B填充到A的b属性上;
5 把A装入一级缓存,并把A从二级、三级缓存移除;
到这里,我们的循环依赖问题就解决了。
代码片段 initializeBean
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { //省略代码... ... //执行了几个aware接口 this.invokeAwareMethods(beanName, bean); Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //执行beanPostProcessor的before接口 wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName); } try { //反射的方式执行初始化方法 this.invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable var6) { throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6); } if (mbd == null || !mbd.isSynthetic()) { //执行beanPostProcessor的after接口 wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
总结
这篇文章主要讲解了依赖注入的过程,以及依赖注入时如果产生了循环依赖的解决办法。但是上面还有一个问题没有解决,就是三级缓存,上面的解决循环依赖时已经说到了一级缓存和二级缓存的作用,但是没有说三级缓存的作用,三级缓存做什么的,为什么是一个Lambda,这个Lambda传入的方法到底做了什么,关于三级缓存先别急,我们会在下一篇文章《SpringBoot源码加餐--AOP》中详细讲解。
浙公网安备 33010602011771号