Spring 循环依赖以及三级缓存

1. 循环依赖问题

  spring创建一个Bean实例分为三步,实例化依赖注入初始化

  实例化的方式分为两大类:工厂方法(静态工厂方法和实例工厂) 和 构造器方法(默认和自动装配)。以一个例子来说下,一个类AppleTree,有一个成员变量Apple 

public class Apple {
    Apple() {

    }
}

public class AppleTree {
    private Apple apple;

    AppleTree(Apple apple) {
        this.apple = apple;
    }

    AppleTree() {

    }
}

//工厂类
public class AppleTreeFactory {
    //静态工厂方法
    public static AppleTree getAppleTree() {
        return new AppleTree(new Apple());
    }

    public AppleTree makeAppleTree() {
        return new AppleTree(new Apple());
    }

}

  我们实例化AppleTree有4种方式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context/
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--    <context:annotation-config/>-->

    <bean id="apple" class="com.jd.java.easycoding.spring.Apple"/>

    <!-- 根据构造器自动装配 -->
    <bean id="appleTree1" class="com.jd.java.easycoding.spring.AppleTree" autowire="constructor"/>

    <!-- 构造器 -->
    <bean id="appleTree2" class="com.jd.java.easycoding.spring.AppleTree">
        <constructor-arg ref="apple"/>
    </bean>

    <!-- 静态工厂方法 -->
    <bean id="appleTree3" class="com.jd.java.easycoding.spring.AppleTreeFactory" factory-method="getAppleTree"/>

    <!-- 工厂实例 -->
    <bean id="appleTree4" class="com.jd.java.easycoding.spring.AppleTree" factory-bean="appleTreeFactory"
          factory-method="makeAppleTree"/>
    <bean id="appleTreeFactory" class="com.jd.java.easycoding.spring.AppleTreeFactory"/>
</beans>

 

  Bean创建的第二步是进行依赖注入。

  依赖注入的方式也有两大类:有参构造器注入set方法。上面实例化的过程中,如果已经通过有参构造器注入了Apple,则不需要再次注入。如果没有注入,比如通过无参构造器创建实例,那么需要进行属性注入,这时就必须在类中定义对应属性的set方法:我们在AppleTree类中添加一个无参构造器和setApple方法来实验一下

public class AppleTree {
    private Apple apple;

    AppleTree(Apple apple) {
        this.apple = apple;
    }

    AppleTree() {

    }

    public void setApple(Apple apple) {
        this.apple = apple;
    }

}

  在xml中添加如下配置

    <!-- set方法 -->
    <bean id="appleTree5" class="com.jd.java.easycoding.spring.AppleTree">
        <property name="apple" ref="apple"/>
    </bean>

  这种方式,实例化时使用的默认无参构造器,然后在依赖注入时,使用setApple方法将Apple这个Bean注入。如果是公有方法使用JDK内省技术获取set方法,如果是私有方法也没有关系,使用反射暴力获取。

  当然还可以使用自动装配特性,有两种,byName和byType,也是通过set方法进行注入的

    <!-- 自动装配byName -->
    <bean id="appleTree6" class="com.jd.java.easycoding.spring.AppleTree" autowire="byName" />

    <!-- 自动装配byType -->
    <bean id="appleTree7" class="com.jd.java.easycoding.spring.AppleTree" autowire="byType" />

  

  下面说下循环依赖的问题,有两个类,ObjectA和ObjectB相互引用。

  循环依赖是发生在依赖注入时候的问题,上面已经解释了依赖注入的方式有构造器注入和set注入。

  1. 先看下构造器注入的方式  

public class ObjectA {
    ObjectB b;

    ObjectA(ObjectB b){
        this.b = b;
    }
}

public class ObjectB {
    ObjectA a;

    ObjectB(ObjectA a) {
        this.a = a;
    }
}

  在xml中使用构造器自动装配

   <!-- 对象A -->
    <bean id="objectA" class="com.jd.java.easycoding.spring.ObjectA" autowire="constructor"/>
    <!-- 对象B -->
    <bean id="objectB" class="com.jd.java.easycoding.spring.ObjectB" autowire="constructor"/>

  在本地进行如下测试,直接一行红字

public class AutoWireTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:bean.xml"});
        ObjectA a = (ObjectA) context.getBean("objectA");
    }
}

 

  2. 尝试通过set方法注入

public class ObjectA {
    ObjectB b;

    ObjectA(ObjectB b) {
        this.b = b;
    }

    ObjectA() {

    }

    public void setB(ObjectB b) {
        this.b = b;
    }
}

public class ObjectB {
    ObjectA a;

    ObjectB(ObjectA a) {
        this.a = a;
    }

    ObjectB() {

    }

    public void setObjectA(ObjectA a) {
        this.a = a;
    }
}

  在xml中使用set方式注入

    <!-- 对象A -->
    <bean id="objectA" class="com.jd.java.easycoding.spring.ObjectA" autowire="byName"/>
    <!-- 对象B -->
    <bean id="objectB" class="com.jd.java.easycoding.spring.ObjectB" autowire="byName"/>

  在本地进行测试,神奇的情况发生了,没有报错。

  此处黑人问号?          

  

2.三级缓存  

  三级缓存是什么?

  1.  private final Map<String, Object> singletonObjects 一级缓存,缓存单例对象的Bean,是已经完全创建完成的Bean

  2.  private final Map<String, Object> earlySingletonObjects 二级缓存,提前曝光半成品单例对象或对象的代理

  3.  private final Map<String, ObjectFactory<?>> singletonFactories 三级缓存,缓存产生单例对象的工厂,通过该工厂能获取到创建一半的Bean

  同一时刻,一个Bean只会存放在一个缓存中

  2.1 获取Bean的流程

  AbstractBeanFactory#doGetBean方法,先通过Bean的名字从缓存中获取单例Bean: Object sharedInstance = getSingleton(beanName); 。这里有一点要注意,如果取出来的是FactoryBean,那么还需要通过FactoryBean的getObject方法来创建对应的Bean(或者从factoryBeanObjectCache缓存中拿)。

  有关FactoryBean和BeanFactory:https://www.cnblogs.com/tiancai/p/9604040.html

  DefaultSingletonBeanRegistry#getSingleton方法

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存获取单例对象
    Object singletonObject = this.singletonObjects.get(beanName);
    //isSingletonCurrentlyInCreation方法:
    //判断当前单例bean是否正在创建中,还没完全创建完毕。这个标识在开始创建Bean时会设置,下面会见到
    //比如一个对象依赖另一个对象,在依赖注入时,需要先创建另一个对象
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            //从二级缓存中获取单例bean
            singletonObject = this.earlySingletonObjects.get(beanName);
            //allowEarlyReference: 是否允许从singletonFactories中通过getObject拿到对象
            if (singletonObject == null && allowEarlyReference) {
                //从三级缓存中获取单例bean
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //通过单例工厂获取单例bean
                    singletonObject = singletonFactory.getObject();
                    //从三级缓存移动到了二级缓存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    //从三级缓存中删除
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}

 

  依次从一、二、三级缓存中获取Bean。如果没有获取到Bean,那么获取BeanDefinition,准备创建,从BeanDefinition中得知这个Bean是单例原型还是等等有不同的创建方式。我们看单例的创建模式

  AbstractBeanFactory#doGetBean方法中的一小段:

if (mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () -> {
        try {
            // 真正创建单例Bean的方法 AbstractBeanFactory#createBean
            // 这种写法是Java8的函数式接口,省去了创建对象
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            destroySingleton(beanName);
            throw ex;
        }
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

 

  DefaultSingletonBeanRegistry#getSingleton

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) {
            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 (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            // 创建之前,设置一个创建中的标识
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                // 调用匿名内部类获取单例对象,完成Bean的创建
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                // 创建成功之后,删除创建中的标识
                afterSingletonCreation(beanName);
            }
            // 将产生的单例Bean放入一级缓存中,并从二三级缓存中删除
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

 

  Bean创建完成,会将Bean放入一级缓存,并从二三级缓存中删除。结合获取Bean的方式,发现在没有循环依赖的情况下,只会从一级缓存中获取Bean,二三级缓存是用不到的。

  我们再来看创建Bean的方法。调用链有点长,直接定位到

  AbstractAutowireCapableBeanFactory#doCreateBean

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

    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        //单例Bean先从缓存中获取同名Bean
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    // bean初始化第一步实例化Bean
    // 构造参数依赖注入,就是发生在这一步
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // 实例化后的Bean对象
    final Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }


    // 解决循环依赖的关键步骤
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    // 如果需要提前暴露单例Bean
    if (earlySingletonExposure) {
        if (logger.isDebugEnabled()) {
            logger.debug("Eagerly caching bean '" + beanName +
                    "' to allow for resolving potential circular references");
        }
        // 在发生循环引用的情况下,将刚创建的bean放入三级缓存中(key是beanName,value是FactoryBean)
        // 通过三级缓存获取Bean,调用的是getEarlyBeanReference方法
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // 记录一下,初始化可能返回的是代理
    Object exposedObject = bean;
    try {
        // bean初始化第二步依赖注入
        // 调用反射和内省去进行属性设置
        // 属性值需要进行类型转换
        populateBean(beanName, mbd, instanceWrapper);
        // bean初始化第三步初始化,完成bean的初始化操作(AOP发生在此步骤
        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);
        }
    }

    if (earlySingletonExposure) {
        // 从二级缓存中拿到先加载类的代理/本身
        Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            // 如果有循环引用,代理是会提前生成的,这就导致了先加载类在初始化时不再生成代理,也就导致了下面的判断成立
            if (exposedObject == bean) {
                // 用代理替换,否则返回的是本身的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 " +
                                    "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                }
            }
        }
    }

    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
        throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}

 

  再用图描述下解决循环引用的过程。  

  

  到此就解决了set方式下循环依赖的问题。

  问题1:为什么构造器注入不能通过这种方式解决循环依赖?

  :因为构造器注入发生在实例化阶段,可以看到B在实例化时还没有去获取A的缓存的。

  问题2:为什么要三级缓存呢?

  :我们不妨从结果看问题,三级缓存的是工厂,二级缓存的是Bean的代理。想想如果没有用到代理呢?那么二级缓存的就是Bean本身,这和三层缓存产生Bean的工厂实际是一样的,也就是在没有代理的情况,只需要二级缓存就足够了,三级缓存的目的是为了多一层来缓存代理。

  问题3:三级缓存的存储时机?

  :一级缓存在Bean完成初始化之后存入,二级缓存在第一次从三级缓存的工厂中获取Bean后存入,三级缓存在Bean实例化完成后且存在循环依赖时存入。

 

 

参考:

1.Bean实例化方式:https://www.jianshu.com/p/646c1f657144,https://rumenz.com/java-topic/spring-core/spring-autowiring-by-constructor/index.html

posted @ 2021-04-10 23:26  walker993  阅读(294)  评论(0编辑  收藏  举报