zoukankan      html  css  js  c++  java
  • Spring源码解析(五)循环依赖问题

    引言

    循环依赖就是多个类之间互相依赖,比如A依赖B,B也依赖A,如果日常开发中我们用new的方式创建对象,这种循环依赖就会导致不断的在创建对象,导致内存溢出。

    Spring是怎么解决循环依赖的问题的?我们结合Spring源码来看一下。

    第一种:构造器参数循环引用,单例

    创建两个测试类:

     1 public class UserServiceImpl implements UserService {
     2     private TestService testService;
     3 
     4     public UserServiceImpl() {
     5     }
     6 
     7     public UserServiceImpl(TestService testService) {
     8         this.testService = testService;
     9     }
    10 }
    public class TestServiceImpl implements TestService {
        private UserService userService;
    
        public TestServiceImpl() {
    
        }
    
        public TestServiceImpl(UserService userService) {
            this.userService = userService;
        }
    
    }

    Spring 配置文件:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xsi:schemaLocation="http://www.springframework.org/schema/beans
     5            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
     6 
     7     <bean id="userService" class="org.springframework.example.service.impl.UserServiceImpl" lazy-init="true">
     8         <constructor-arg name="testService" ref="testService"></constructor-arg>
     9     </bean>
    10     <bean id="testService" class="org.springframework.example.service.impl.TestServiceImpl" lazy-init="true">
    11         <constructor-arg name="userService" ref="userService"></constructor-arg>
    12     </bean>
    13 </beans>

    测试类:

    public class ApplicationContextTest {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("classpath*:test*.xml");
            TestService testService= (TestService) context.getBean("testService");
            System.out.println(testService);
        }
    }

    例子很简单,两个类互相引用,通过构造器的方式初始化Bean并且两个都是单例。

    我们看一下测试结果:

    Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testService' defined in file [D:spring-framework-3.2.15.RELEASEspring-framework-3.2.15.RELEASEspring-testuild
    esourcesmain	est1.xml]: Cannot resolve reference to bean 'userService' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in file [D:spring-framework-3.2.15.RELEASEspring-framework-3.2.15.RELEASEspring-testuild
    esourcesmain	est1.xml]: Cannot resolve reference to bean 'testService' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:336)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:652)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1109)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1009)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:492)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:301)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191)
        at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1132)
        at org.springframework.example.test.ApplicationContextTest.main(ApplicationContextTest.java:22)
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in file [D:spring-framework-3.2.15.RELEASEspring-framework-3.2.15.RELEASEspring-testuild
    esourcesmain	est1.xml]: Cannot resolve reference to bean 'testService' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:336)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:652)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1109)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1009)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:492)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:301)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
        ... 13 more
    Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testService': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:327)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:217)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191)
        at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)    

     下面通过源码分析一下原因:

    在spring中创建单例Bean调用的是DefaultSingletonBeanRegistry,我们直接看DefaultSingletonBeanRegistry就好了。

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
            Assert.notNull(beanName, "'beanName' 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 the 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 + "'");
                    }
                    /**1.创建bean之前singletonsCurrentlyInCreation put BeanName,该bean正在创建中*/
                    beforeSingletonCreation(beanName);
                    boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                    if (recordSuppressedExceptions) {
                        this.suppressedExceptions = new LinkedHashSet<Exception>();
                    }
                    try {
                        /**2.创建Bean*/
                        singletonObject = singletonFactory.getObject();
                    }
                    catch (BeanCreationException ex) {
                        if (recordSuppressedExceptions) {
                            for (Exception suppressedException : this.suppressedExceptions) {
                                ex.addRelatedCause(suppressedException);
                            }
                        }
                        throw ex;
                    }
                    finally {
                        if (recordSuppressedExceptions) {
                            this.suppressedExceptions = null;
                        }
                        /**3.bean创建完成以后singletonsCurrentlyInCreation删除beanName*/
                        afterSingletonCreation(beanName);
                    }
                    /**4.将单例Bean添加容器中*/
                    addSingleton(beanName, singletonObject);
                }
                return (singletonObject != NULL_OBJECT ? singletonObject : null);
            }
        }

    1.创建bean之前判断该bean是否正在创建中

    protected void beforeSingletonCreation(String beanName) {
            if (!this.inCreationCheckExclusions.containsKey(beanName) &&
                    this.singletonsCurrentlyInCreation.put(beanName, Boolean.TRUE) != null) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
        }

    singletonsCurrentlyInCreation就是一个Map,如果第一次创建Bean,会将beanName添加到map中并返回前一个值null。比如我们现在先初始化UserService,那么map中就会存在<userService,true>
    2.通过constructor初始化Bean,BeanDefinitionValueResolver
    /**
         * Resolve a reference to another bean in the factory.
         */
        private Object resolveReference(Object argName, RuntimeBeanReference ref) {
            try {
                String refName = ref.getBeanName();
                refName = String.valueOf(evaluate(refName));
                if (ref.isToParent()) {
                    if (this.beanFactory.getParentBeanFactory() == null) {
                        throw new BeanCreationException(
                                this.beanDefinition.getResourceDescription(), this.beanName,
                                "Can't resolve reference to bean '" + refName +
                                "' in parent factory: no parent factory available");
                    }
                    return this.beanFactory.getParentBeanFactory().getBean(refName);
                }
                else {
                    /**先初始化ref引用的bean*/
                    Object bean = this.beanFactory.getBean(refName);
                    this.beanFactory.registerDependentBean(refName, this.beanName);
                    return bean;
                }
            }
            catch (BeansException ex) {
                throw new BeanCreationException(
                        this.beanDefinition.getResourceDescription(), this.beanName,
                        "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
            }
        }
    UserService还没有创建完成,需要先创建TestService,创建TestService又会走第1步,将testService添加到map中<testService,true>,这个时候来到第2步,又会先创建UserService,这个时候又到流程1,
    发现Map中已存在<userService,true>,这个时候就会抛出BeanCurrentlyInCreationException。

    第二种:set方法方式,单例

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xsi:schemaLocation="http://www.springframework.org/schema/beans
     5            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
     6 
     7     <bean id="userService" class="org.springframework.example.service.impl.UserServiceImpl" lazy-init="true">
     8         <property name="testService" ref="testService"></property>
     9     </bean>
    10     <bean id="testService" class="org.springframework.example.service.impl.TestServiceImpl" lazy-init="true">
    11         <property name="userService" ref="userService"></property>
    12     </bean>
    13 </beans>
    单例的实例化过程主要分为两步:创建bean实例,属性的依赖注入,之前通过带参数constructor实例化的时候,需要先实例化属性,所以导致了循环依赖。
    set方法注入的方式是怎么避免的呢?接下来看一下AbstractAutowireCapableBeanFacotry:
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
            //省略.....if (instanceWrapper == null) {
                /**1.创建bean实例*/
                instanceWrapper = createBeanInstance(beanName, mbd, args);//通过Constructor初始化在此处就训陷入循环引用
            }
            //省略....................// Eagerly cache singletons to be able to resolve circular references
            // even when triggered by lifecycle interfaces like BeanFactoryAware.
            /**
             * 2.及早暴露单例bean引用,解决循环引用
             */
            boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
                //创建匿名内部类,匿名内部类会将beanName、mbd,bean(final修饰)拷贝一份到内部类,已保证当前方法执行完成后局部变量退栈后内部类还可以访问。
                addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    public Object getObject() throws BeansException {
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });
            }
    
            // Initialize the bean instance.
            Object exposedObject = bean;
            try {
                /**
                 * 3.依赖注入
                 */
                populateBean(beanName, mbd, instanceWrapper);
                if (exposedObject != null) {
                    /**
                     * 9.执行自定义BeanProcesser和init-method
                     */
                    exposedObject = initializeBean(beanName, exposedObject, mbd);
                }
            }
         //省略.......
    1.创建bean实例时,用的无参构造器属性且属性并没有注入值。
    2.及早暴露单例bean引用,这一步就是解决循环引用的关键。
    /** Cache of singleton objects: bean name --> bean instance */
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
    
        /** Cache of singleton factories: bean name --> ObjectFactory */
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
    
        /** Cache of early singleton objects: bean name --> bean instance */
        private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
    
        /** Set of registered singletons, containing the bean names in registration order */
        private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
    
    /**
         * Add the given singleton factory for building the specified singleton
         * if necessary.
         * <p>To be called for eager registration of singletons, e.g. to be able to
         * resolve circular references.
         * @param beanName the name of the bean
         * @param singletonFactory the factory for the singleton object
         */
        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);
                }
            }
        }
    this.singletonFactories.put(beanName, singletonFactory);这里把匿名内部类放到了map里,匿名内部类会将beanName、mbd,bean(final修饰)拷贝一份到内部类,通过
    singletonFactory的getEarlyBeanReference方法可以获得未初始化完成的bean实例。这就相当于把未初始化完成的bean提前暴露出来。
    /**
         * Obtain a reference for early access to the specified bean,
         * typically for the purpose of resolving a circular reference.
         * @param beanName the name of the bean (for error handling purposes)
         * @param mbd the merged bean definition for the bean
         * @param bean the raw bean instance
         * @return the object to expose as bean reference
         */
        protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
            Object exposedObject = bean;
         //这是扩展点,如果不扩展相当于直接返回bean
    if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); if (exposedObject == null) { return exposedObject; } } } } return exposedObject; }
    3.依赖注入
    bean创建完成后要为属性注入值
        /**先初始化ref引用的bean*/
                    Object bean = this.beanFactory.getBean(refName);
                    this.beanFactory.registerDependentBean(refName, this.beanName);
                    return bean;
    例如当前初始化的是A依赖B,
    这个时候就会初始化依赖对象B,B初始化过程跟A是一样的,也会到依赖注入这一步,这个时候同样会初始化依赖对象A,bean在初始化之前会先在缓冲中取,这个时候就可以取到A,就不会循环继续初始化下去。
    /**
         * Return the (raw) singleton object registered under the given name.
         * <p>Checks already instantiated singletons and also allows for an early
         * reference to a currently created singleton (resolving a circular reference).
         * @param beanName the name of the bean to look for
         * @param allowEarlyReference whether early references should be created or not
         * @return the registered singleton object, or {@code null} if none found
         */
        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                    singletonObject = this.earlySingletonObjects.get(beanName);
                    if (singletonObject == null && allowEarlyReference) {
                        ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                        if (singletonFactory != null) {
                            singletonObject = singletonFactory.getObject();
                            //单例bean只是创建了实例,并没有为属性赋值,提前暴露出来,防止循环依赖
                            this.earlySingletonObjects.put(beanName, singletonObject);
                            this.singletonFactories.remove(beanName);
                        }
                    }
                }
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }

     总结:

    初始化A对象:
        --创建A对象
        --将包含A对象的匿名内部对象ObjectFacotry放到singletonFactories(map)中。
        --依赖注入,创建对象B
                  --创建B对象
                  --将包含B对象的匿名内部对象ObjectFacotry放到singletonFactories(map)中。
                  --依赖注入,创建对象A
                        --创建之前,判断缓存中是否存在
                          singletonFactory.getObject()获取A,将对象A放到earlySingletonObjects(Map)中,并返回
                  --B对象创建完毕,放到singletonObjects(Map)中
        --A对象创建完毕,放到singletonObjects(Map)中
    初始化B对象:
        --创建前,判断缓存中是否存在
          Object singletonObject = this.singletonObjects.get(beanName);直接返回B

    第三种:set方法方式和constructor混合,单例

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
    
        <bean id="userService" class="org.springframework.example.service.impl.UserServiceImpl" lazy-init="true" >
           <constructor-arg ref="testService" index="0"></constructor-arg>
        </bean>
        <bean id="testService" class="org.springframework.example.service.impl.TestServiceImpl" lazy-init="true">
            <property name="userService" ref="userService"></property>
        </bean>
    </beans>
    我们通过单例Bean初始化过程的梳理,通过默认构造器初始化的单例Bean会提前暴露出来,其他bean引用它的时候是可以直接获取到的。
    现在这种配置情况,context.getBean("userService");是会抛出循环引用异常的,而context.getBean("testService")是正常的。
    我们看一下测试结果:

    第四种:set方法方式,prototype

    prototype类型的bean循环依赖会报异常。通过源码看一下,这个比较简单

          /**prototype类型,判断benaName是不是创建中,出现了循环引用*/
                if (isPrototypeCurrentlyInCreation(beanName)) {
                    throw new BeanCurrentlyInCreationException(beanName);
                }
            //省略.......         
    /** * 6.创建原型bean,scope="prototype" */ else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); }
        /** Names of beans that are currently in creation */
        private final ThreadLocal<Object> prototypesCurrentlyInCreation =
                new NamedThreadLocal<Object>("Prototype beans currently in creation");
    
        protected void beforePrototypeCreation(String beanName) {
            Object curVal = this.prototypesCurrentlyInCreation.get();
            if (curVal == null) {
                this.prototypesCurrentlyInCreation.set(beanName);
            }
            else if (curVal instanceof String) {
                Set<String> beanNameSet = new HashSet<String>(2);
                beanNameSet.add((String) curVal);
                beanNameSet.add(beanName);
                this.prototypesCurrentlyInCreation.set(beanNameSet);
            }
            else {
                Set<String> beanNameSet = (Set<String>) curVal;
                beanNameSet.add(beanName);
            }
        }

    在创建prototype类型的bean之前会将beanName添加到prototypesCurrentlyInCreation,prototypesCurrentlyInCreation是ThreadLocal类型的,每个线程一个。所有创建中的BeanName都会放进去。

    创建A对象

        --判断benaName是不是创建中

        --A 添加到prototypesCurrentlyInCreation

        --创建A实例

        --依赖注入

            --创建依赖B

              --判断benaName是不是创建中

              --B添加到prototypesCurrentlyInCreation(此时prototypesCurrentlyInCreation对应的value值是个Set 包含了A和B)

              --创建A实例

                --判断benaName是不是创建中,这个时候就会抛出BeanCurrentlyInCreationException异常        

  • 相关阅读:
    web页面常用方法及INI文件的读取方法
    winform 三个Panel左右切换(panel里面填充图片)
    图片渐出轮播的效果
    Winform跑马灯——Graphics运用
    .net 3.5 新功能重写ToInt()方法
    style.display
    SQL: 分页SQL SQL2005函数分页!
    JS: 验证输入必须为数字
    Table 里面点标题会进行排序
    在Div中绑定数据
  • 原文地址:https://www.cnblogs.com/monkey0307/p/8617143.html
Copyright © 2011-2022 走看看