• 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

    人生就像蒲公英,看似自由,其实身不由己。
  • 相关阅读:
    OneProxy与其它数据库中间件的对比
    防御式编程
    google jam 比赛题(设计有问题)
    Python 代码性能优化技巧
    Python性能鸡汤
    如何避免重构带来的危险
    Linux/Unix工具与正则表达式的POSIX规范
    代码抽象层次2
    chinaunix:腾讯面试题
    C++异常处理小例
  • 原文地址:https://www.cnblogs.com/walker993/p/14642603.html
Copyright © 2020-2023  润新知