• Spring源代码解析 ---- 循环依赖


    一、循环引用


    1. 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比方CircularityA引用CircularityB,CircularityB引用CircularityC,CircularityC引用CircularityA。形成一个环状引用关系。




    2. 代码演示样例:

    CircularityA

    public class CircularityA {  
        private CircularityB circularityB;  
    	
        public CircularityA() {  
        }  
    	
        public CircularityA(CircularityB circularityB) {  
            this.circularityB = circularityB;  
        }  
    	
    	public void setCircularityB(CircularityB circularityB) {  
    		this.circularityB = circularityB;  
    	}  
    	
    	public void a() {  
    	   circularityB.b();  
    	}  
    }


    CircularityB

    public class CircularityB {  
        private CircularityC circularityC;  
    	
        public CircularityB() {  
        }  
    	
        public CircularityB(CircularityC circularityC) {  
            this.circularityC = circularityC;  
        }  
    	
    	public void setCircularityC(CircularityC circularityC) {  
            this.circularityC = circularityC;  
        }  
    	
        public void b() {  
            circularityC.c();  
        }  
    }  


    CircularityC

    public class CircularityC {  
        private CircularityA circularityA;  
    	
        public CircularityC() {  
        }
    	
        public CircularityC(CircularityA circularityA) {  
            this.circularityA = circularityA;  
        }  
    	
    	public void setCircularityC(CircularityA circularityA) {  
            this.circularityA = circularityA;  
        }  
    	
        public void c() {  
            circularityA.a();  
        }  
    }  


    3. Spring源代码:

    在Spring源代码的AbstractAutowireCapableBeanFactory类中有例如以下代码:

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    		// Instantiate the bean.
    	    // 忽略此处代码
    	    
    
    		// 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.isDebugEnabled()) {
    				logger.debug("Eagerly caching bean '" + beanName +
    						"' to allow for resolving potential circular references");
    			}
    			addSingletonFactory(beanName, new ObjectFactory() {
    				public Object getObject() throws BeansException {
    					return getEarlyBeanReference(beanName, mbd, bean);
    				}
    			});
    		}
    
    		// 下面代码忽略
    	}


    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    		Object exposedObject = 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;
    	}


    这是Spring真正创建Bean的地方, 可是创建Bean就要考虑到处理循环引用又叫做循环依赖的问题。

    代码中写到了对单例Bean循环依赖的处理。 大致就是用递归的方法找出当前Bean的所有依赖Bean, 然后所有提前缓存起来。


    setter循环依赖(对于setter注入造成的依赖是通过Spring容器提前暴露刚完毕构造器注入但未完毕其它步骤(如setter注入)的Bean来完毕的,并且仅仅能解决单例作用域的Bean循环依赖)详细处理过程例如以下:

           (1) Spring容器创建单例“circularityA” Bean。首先依据无參构造器创建“circularityA” Bean, 并暴露一个exposedObject用于返回提前暴露的Bean。并将“circularityA”Bean放到Catch中。然后进行setter注入“circularityB”;


           (2) Spring容器创建单例“circularityB" Bean。首先依据无參构造器创建“circularityB" Bean,并暴露一个exposedObject用于返回提前暴露的Bean。并将“circularityB” Bean放到Catch中,然后进行setter注入“circularityC”;


           (3) Spring容器创建单例“circularityC” Bean,首先依据无參构造器创建“circularityC” Bean,并暴露一个exposedObject用于返回暴露的Bean。并将“circularityC” Bean放入Catch中, 然后进行setter注入“circularityA”。进行注入“circularityA”时因为步骤1提前暴露了exposedObject所以从之前的catch里面拿Bean不用反复创建。


           (4) 最后在依赖注入“circularityB”和“circularityA”也是从catch里面拿提前暴露的bean。  完毕setter注入。
     
           可是对于“prototype”作用域Bean。Spring容器无法完毕依赖注入,由于“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。



    另一种Spring无法解决的循环依赖方式----构造器循环依赖

    如在创建CircularityA类时,构造器须要CircularityB类。那将去创建CircularityB,在创建CircularityB类时又发现须要CircularityC类,则又去创建CircularityC,终于在创建CircularityC时发现又须要CircularityA。 形成环状依赖, 从而被Spring抛出。

    Spring容器将每个正在创建的Bean 标识符放在一个“当前创建Bean池”中,Bean标识符在创建过程中将一直保持在这个池中,因此假设在创建Bean过程中发现自己已经在“当前创建Bean池”里时将抛出BeanCurrentlyInCreationException异常表示循环依赖。而对于创建完成的Bean将从“当前创建Bean池”中清除掉。




    二、 循环调用


    1. 定义: 循环调用事实上就是一个死循环(这个是无法解决仅仅能提前避免), 终于造成StackOverflow。



    2. 工作实例:

      在做Hadoop的调度中间件的时候以前出现过这一个问题, 用到的解决方法事实上和Spring的解决循环依赖的思想非常相似。 Spring用的是缓存, 我们当时用的是一个集合类。


      Hadoop工作有一个常见流程: A -->  B --> C


    A、B、C是必须符合前后顺序的。 可是业务系统的人可能在创建这样的顺序时建成A --> B --> C --> A形成一个环状。 那么这就是一种循环调用。

    解决思想及时在建立关系时把A、B、C创建的时候就丢入集合类。 假设发现反复那么说明肯定存在某种环在里面。 然后做出对应处理。 就把循环调用提前阻止了。

     



  • 相关阅读:
    Servlet再度学习
    JSP九大内置对象
    Java I/O学习
    Java内存管理
    数据库面试常问的一些基本概念
    JVM类加载原理学习笔记
    Ajax原理学习
    Java基础之泛型
    Java基础之集合
    java多线程快速入门(二)
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5208076.html
Copyright © 2020-2023  润新知