月夜星空-liulq

--愿美好接踵而至

前言

   本章节开始前先回忆一下现在我们都做了哪些工作,首先定义几个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》中详细讲解。

 

 

 

 

 

 



posted on 2022-01-16 17:13  月夜星空-liulq  阅读(270)  评论(0)    收藏  举报