美文网首页
spring 循环依赖处理流程

spring 循环依赖处理流程

作者: hello_kd | 来源:发表于2020-09-12 17:38 被阅读0次

在应用程序中,经常会有循环依赖的场景,比如A引用了B,B引用了A,spring对于这种情况的处理是通过三级缓存来实现的,其实就是内部维护了三个map

    /** 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 ConcurrentHashMap<>(16);

下面以这样的实例来跟踪下spring的处理流程,来看下spring是如何利用三级缓存来实现循环依赖的,这个例子中Student引用了Teacher,而Teacher引用了Student,且还存在一个切面类针对Student的examScore方法

@Component
public class Student {
    private String name;
    @Autowired
    private Teacher teacher;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public int examScore() {
        System.out.println("student examScore");
        return 1;
    }
}

@Component
public class Teacher {
    private String name;

    @Autowired
    private Student student;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }
}

@Aspect
@Component
public class StudentAspect {
    @Pointcut("execution(* com.ailra.beans.Student.examScore(..))")
    public void studentCut() {
    }

    @Before("studentCut()")
    public void before() {
        System.out.println("before");
    }
}

应用程序这时要获取一个Student的bean实例时,会执行如下这个方法

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 先看spring的一级缓存是否已经存在了,若存在了,直接返回
        Object singletonObject = this.singletonObjects.get(beanName);
//如果不存在,且还在创建的过程中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                synchronized (this.singletonObjects) {
                    // Consistent creation of early reference within full singleton lock
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        singletonObject = this.earlySingletonObjects.get(beanName);
                        if (singletonObject == null) {
                            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                            if (singletonFactory != null) {
                                singletonObject = singletonFactory.getObject();
                                this.earlySingletonObjects.put(beanName, singletonObject);
                                this.singletonFactories.remove(beanName);
                            }
                        }
                    }
                }
            }
        }
        return singletonObject;
    }

刚开始时,在 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName))这个判断就会返回false,因为此刻student单例还没被创建,且还未开始

程序继续往后执行,在执行创建对象的工厂方法前后会分别执行这两个方法

//执行创建对象方法前,先将beanName加到singletonsCurrentlyInCreation,表示当前bean实例正在创建中
    protected void beforeSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
    }
//当bean实例创建完毕后,将beanName从singletonsCurrentlyInCreation移除,表示bean不处于创建过程中
    protected void afterSingletonCreation(String beanName) {
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }

所以判断bean是否创建过程,只需要看是否存在singletonsCurrentlyInCreation集合中,也是getSingleton最开始的判断条件之一

接着,一直跟踪到这个方法doCreateBean

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {

    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
//这里创建好bean的对象了,此时对象的属性值还未赋值
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }


    // Eagerly cache singletons to be able to resolve circular references
    // even when triggered by lifecycle interfaces like BeanFactoryAware.
    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));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
    }

    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!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.");
                }
            }
        }
    }
    return exposedObject;
}

这个方法是循环依赖最核心的部分,其中省略了部分代码,这里分成以下几个步骤讲解

  1. 执行到这个方法的Object bean = instanceWrapper.getWrappedInstance();这行代码时,一个普通的student对象创建出来了,但是其成员变量teacher还未赋值
  2. boolean earlySingletonExposure = ..... 这行代码,早期对象暴露,从后面的条件可以看出,若bean是单例的且允许循环依赖,还有时处于创建过程中的,那么允许将此时的对象暴露出去,但是暴露出去并不是直接将这个对象加到二级缓存的半成品对象map中,而是添加一个对象工厂到三级缓存中,这是因为存在代理对象的情况,也是这个例子中使用切面的原因,这个等会再回过头来看下更清楚些
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

//将工厂方法添加到三级缓存中,并且将其从二级缓存移除
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);
        }
    }
}
//创建对象的工厂方法
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}
  1. populateBean(beanName, mbd, instanceWrapper);执行这行代码时,要对student的属性进行赋值,其中一个属性是teacher引用,所以要先创建一个teacher的实例,所以程序又回到了getSingleton方法
  2. 然后创建teacher过程中,将beanName加入到singletonsCurrentlyInCreation中,表示teacher正在创建,接着程序执行到doCreateBean方法
  3. 此时,teacher执行boolean earlySingletonExposure...这行代码时,也是允许暴露早期对象,因此也将创建teacher实例的工厂添加到三级缓存中addSingletonFactory
  4. 执行populateBean代码时,要对teacher的属性进行赋值,其中就有包含student引用。因此要从spring工厂拿student的单例对象,所以程序此时再次回到getSingleton方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // Quick check for existing instance without full singleton lock
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
            synchronized (this.singletonObjects) {
                // Consistent creation of early reference within full singleton lock
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
        }
    }
    return singletonObject;
}

但这时,这个方法就会执行到最里层判断的代码了,在前面的步骤2中,已经将student的单例创建工厂添加到三级缓存中,并且将其从二级缓存中移除了,且student单例也处于正在创建的过程中。

所以执行创建student单例的工厂方法getEarlyBeanReference

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}

这个方法返回的student对象,会和入参的bean对象不一样,因为student有个切面类,所以返回的是一个代理对象,并加入到earlySingletonObjects中,防止后续有其他对象也引用了这个student对象(比如student对象还有一个属性Car对象,Car对象也引用了Student),每次都要执行工厂方法,影响性能,并且工厂方法返回的对象每次都是不一样的。如student对象有存在一级缓存了,也需要移除下。

  1. 当对teacher的属性都赋值完毕后,程序回到exposedObject = initializeBean(beanName, exposedObject, mbd);这行代码,要对teacher进行初始化操作,执行完初始化操作后,teacher的单例对象就创建完毕了,加入到一级缓存中,后面if (earlySingletonExposure)逻辑后几个步骤解释
  2. 当teacher的单例对象创建完毕后,会将其从singletonsCurrentlyInCreation移除,并且执行addSingleton方法
    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);
        }
    }

因为此时teacher的实例实例化且初始化,所以添加到一级缓存中,并将其从二级三级缓存移除掉。

  1. 当student实例执行完对所有属性都赋值完毕后,程序回到前面的步骤3,然后接着开始执行初始化方法initializeBean
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
//执行实现了aware接口的方法
    invokeAwareMethods(beanName, bean);
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
//初始化前执行的方法
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
//执行初始化方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        
    }
//初始化后执行的方法
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

注意:这个初始化方法的入参bean是普通的student对象,而不是代理对象,而在执行applyBeanPostProcessorsAfterInitialization这个方法时,由于aspectj有加了一个AnnotationAwareAspectJAutoProxyCreator,这也是一个BeanPostProcessor,所以这里就会执行了,返回一个代理对象

  1. 执行完initializeBean后,进入了这个代码片段
if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    if (!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.");
                }
            }
        }
    }

这里又会执行getSingleton,先获取到早期暴露的对象,也就是已经被其他对象引用的实例,然后会执行这个判断if (exposedObject == bean),这是因为exposedObject 有可能在initializeBean方法内被BeanPostProcessor的初始化后的方法给改变了。如果条件判断相等的话,就直接返回早期暴露的对象,如果不相等的话,还会判断这个bean是否被其他bean实例引用了,若有引用,则抛出异常,表示被引用的bean对象与当前最终创建好的bean对象不一致

doCreateBean方法执行完后,再讲bean实例添加到一级缓存中,然后将其从二级和三级缓存移除。

总结一下,spring使用的三级缓存,就是三个map。

  • singletonObjects 存储的是最终的对象
  • singletonFactories 存储的是创建暴露出去给其他对象引用的早期对象的工厂
  • earlySingletonObjects 存储的是暴露出去给其他对象引用的早期对象

那么,为何需要使用3个map?首先一个肯定不行,因为一个要存放的是最终对象,那么中间创建过程中的对象就没地方存储了

如果只使用两个呢?两个的话,我觉得功能上是可以实现的,相当于一个存储最终对象,一个存储的是工厂,每次要获取早期暴露出去的对象,无非是每次都执行一次工厂方法,但是工厂方法内部同样需要使用缓存,不然每次获取的都是不一样的对象了。因此这种方式实际上还是用了三个map来实现,只是第三个放的地方不一样而已

如果两个map,一个存储最终对象,一个存储早期暴露对象,而不是存储工厂呢?这种相当于在addSingletonFactory方法的位置,直接执行工厂方法了,而不是将其缓存,那么执行完这个方法后,马上将早期暴露出去的对象缓存起来。再接着执行后面的populateBean方法,虽然说是可以的,但是相当于说,无论有没存在循环依赖,我都先执行了工厂方法,假如不存在其他对象来引用的话,这个代码执行就多余了,影响性能吧

这里还有需要注意的是,假如不存在循环依赖,那么早期暴露对象对应的工厂方法不会执行,切面类还是会返回代理对象,因为aspectj会添加一个BeanPostProcessor到spring工厂,因此processor的applyBeanPostProcessorsAfterInitialization方法会返回代理对象。但是并不是最终的bean实例就是这个切面类返回的代理对象,因为可能还存在其他BeanPostProcessor来代理这个bean。

上述的观点只是个人的理解,可能存在偏差,有误的话请指出。

相关文章

  • spring 循环依赖处理流程

    在使用Spring开发的时候,如果要在类的某个方法的执行前后添加一些特殊逻辑处理,我们往往会使用aspect或者m...

  • Spring 循环依赖问题fix

    Spring 循环依赖问题fix 拆分的时候,把error都处理完后,准备把工程起起来,发现spring的循环依赖...

  • 一文详解Spring中的循环依赖,面试必杀技

    目录 前言 什么是循环依赖? 什么情况下循环依赖可以被处理? Spring是如何解决的循环依赖? 简单的循环依赖(...

  • Spring 循环依赖处理

    什么是循环依赖 循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 ...

  • spring 循环依赖处理

    解决 bean 之间的循环依赖分为2种: 构造函数注入导致的循环依赖发现这种情况, spring无解, 直接抛出 ...

  • Spring处理循环依赖

    什么是循环依赖 循环依赖指的是多个对象之间的依赖关系形成一个闭环。 下图展示了两个对象 A 和 B 形成的一个循环...

  • Spring 是如何解决循环依赖的?

    Spring 是如何解决循环依赖的? 循环依赖: Spring 循环依赖有三种情况: 构造器的循环依赖,这种依赖 ...

  • Spring-IOC-循环依赖检测与Bean的创建

    Spring容器的循环依赖检测 Spring容器循环依赖包括:构造器循环依赖和setter循环依赖。 1- 构造器...

  • 浅析Spring循环依赖处理

    背景正在学习Spring源码。微信公众号的推文中有一篇循环依赖相关文章,但是只有大概处理过程,没有具体的流程。借此...

  • Spring之循环依赖及解决方式

    1.Spring循环依赖 循环依赖指Spring对象之间的循环引用,最终形成死循环。举例: A依赖于B,B依赖于C...

网友评论

      本文标题:spring 循环依赖处理流程

      本文链接:https://www.haomeiwen.com/subject/xjwjektx.html