zoukankan      html  css  js  c++  java
  • 从源码解读Spring如何解决bean循环依赖

    1 什么是bean的循环依赖

    循环依赖的原文是circular reference,指多个对象相互引用,形成一个闭环。

    以两个对象的循环依赖为例:

    循环依赖

    Spring中的循环依赖有 3 种情况:

    1. 构造器(constructor)的循环依赖;
    2. 字段(field)的循环依赖;
    3. 构造器与字段的循环依赖。

    其中的第 2 、第 3 种情况Spring可以解决,但第 1 情况Spring无法解决。当出现构造器循环依赖的时候,会抛出异常:

    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cat' defined in class path resource [constructor.xml]: Cannot resolve reference to bean 'dog' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dog': Requested bean is currently in creation: Is there an unresolvable circular reference?
    

    The dependencies of some of the beans in the application context form a cycle:
    
    ┌─────┐
    |  securityConfig defined in file [E:IDEAlab-back-end	argetclassescomlpcconfigSecurityConfig.class]
    ↑     ↓
    |  userServiceImpl defined in file [E:IDEAlab-back-end	argetclassescomlpcserviceimplUserServiceImpl.class]
    └─────┘
    

    2 准备

    准备两个POJO

    public class Dog {
        private String name;
        private Cat friend;
    
        public Dog() {
        }
    
        public Dog(String name, Cat friend) {
            this.name = name;
            this.friend = friend;
        }
    
        // 省略getter、setter
    }
    
    public class Cat {
    	private String name;
    	private Dog friend;
    
    	public Cat() {
    	}
    
    	public Cat(String name, Dog friend) {
    		this.name = name;
    		this.friend = friend;
    	}
    
    	// 省略getter、setter
    }
    

    Cat类里有个成员变量的类型是Dog类,同时Dog类里有个成员变量的类型是Cat类,满足了循环依赖的条件。

    3 字段循环依赖的源码分析

    3.1 field.xml

    准备一个field.xml,这个xml配置了两个bean:

    <?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.xsd">
    
    	<bean id="dog" class="Dog">
    		<property name="name" value="dog"/>
    		<property name="friend" ref="cat"/>
    	</bean>
    
    	<bean id="cat" class="Cat">
    		<property name="name" value="cat"/>
    		<property name="friend" ref="dog"/>
    	</bean>
    </beans>
    

    xml里声明了两个bean,通过属性注入的方式将另一个bean引用到自己的成员变量中。在主类中加载xml:

    ClassPathXmlApplicationContext ac
          = new ClassPathXmlApplicationContext("field.xml");
    

    3.2 方法的调用链

    方法调用

    方法调用过程总结可以直接跳转到3.12。

    3.3 AbstractBeanFactory#doGetBean()

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
    			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        // 省略部分代码
    	Object bean;
    	// 重要步骤
    	// 检查单例缓存中是否有手动注册单例bean
    	// 它其实是调了DefaultSingletonBeanRegistry#getSingleton(beanName, true)
    	Object sharedInstance = getSingleton(beanName);
    	if (sharedInstance != null && args == null) {
    		// 省略部分代码
    	}
    	else {
    		// 省略部分代码
            try {
    			// 省略部分代码
                if (mbd.isSingleton()) {
                    // 重要步骤
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            // 重要步骤
                            // singletonFactory.getObject()的具体实现。AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])
                            return createBean(beanName, mbd, args);
                        }
                        // 省略部分代码
                    });
                    // 省略部分代码
                }
                // 省略部分代码
        }
        // 省略部分代码
        return (T) bean;
    }
    

    这里有两段代码是重点。

    • Object sharedInstance = getSingleton(beanName);

      这个方法调用的实际上是getSingleton(beanName, true);。详见3.4。

    • sharedInstance = getSingleton(beanName, () -> {
          try {
              // 重要步骤
              // singletonFactory.getObject()的具体实现。AbstractAutowireCapableBeanFactory#createBean(String, RootBeanDefinition, Object[])
              return createBean(beanName, mbd, args);
          }
          // 省略部分代码
      });
      

      详见3.5。

    3.4 DefaultSingletonBeanRegistry#getSingleton(String, boolean)

    DefaultSingletonBeanRegistry类的三个Map类型的成员变量,这三个成员变量也就是所谓的三级缓存。

    // 一级缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    // 二级缓存
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    // 三级缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    // 除了三级缓存以外,还有一个Set,beanName保存在这个Set里表示这个bean正在创建中
    private final Set<String> singletonsCurrentlyInCreation =
    			Collections.newSetFromMap(new ConcurrentHashMap<>(16));
    
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    	// 三级缓存其实就是三个Map,键是beanName
    	// 1 去一级缓存singletonObjects里获取
    	Object singletonObject = this.singletonObjects.get(beanName);
    	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    		// 如果 没有从一级缓存中获取到,且对象在正在创建中
            // 判断对象正在创建中的方法是判断beanName是不是在singletonCurrentlyInCreation这个Set里
    		synchronized (this.singletonObjects) {
    			// 2 从二级缓存earlySingletonObjects中获取
    			singletonObject = this.earlySingletonObjects.get(beanName);
    			if (singletonObject == null && allowEarlyReference) {
    				// 如果 二级缓存中没有获取到,且allowEarlyReference为true
    				// 3.1 从三级缓存中获取,这时候获取到的是ObjectFactory,这是个工厂,并不是我们要实例
    				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    				if (singletonFactory != null) {
    					// 3.2 从对象工厂中获取实例
    					singletonObject = singletonFactory.getObject();
    					// 3.3 将获取到的实例保存到二级缓存(为什么不用保存到一级缓存)
    					this.earlySingletonObjects.put(beanName, singletonObject);
    					// 3.4 将工厂从三级缓存中移除
    					this.singletonFactories.remove(beanName);
    				}
    			}
    		}
    	}
    	return singletonObject;
    }
    

    3.5 DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)

    3.5其实是3.4的重载。

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    		// 省略部分代码
    		synchronized (this.singletonObjects) {
    		// 去一级缓存中拿bean
    		Object singletonObject = this.singletonObjects.get(beanName);
    		if (singletonObject == null) {
    			// 省略部分代码
    
    			// 重要步骤
    			// 这个方法会将beanName添加到singletonsCurrentlyInCreation这个Set中
    			// 这步添加操作会在后面调用getSingletion(String, boolean)的时候起作用
    			beforeSingletonCreation(beanName);
    			
    			// 省略部分代码
    
    			try {
    				// 重要步骤
    				// 在这一步创建了对象
    				// singletonFactory是作为参数传进来的匿名内部类
    				singletonObject = singletonFactory.getObject();
    				// 省略部分代码
    			} 
    			// 省略部分代码
    		}
    		return singletonObject;
    	}
    }
    

    这里有两段代码是重点。

    • beforeSingletonCreation(beanName);

      这个方法会将beanName添加到singletonsCurrentlyInCreation这个Set中。方法见3.6。singletonsCurrentlyInCreation的定义见3.4。

    • singletonObject = singletonFactory.getObject();

      singletonFactory是方法的参数,实际上是3.3第二段重要代码中用lambda创建的匿名内部类,实际上调用的是AbstractAutowireCapableBeanFactory类的doCreateBean(String, RootBeanDefinition, jObject[])

    3.6 DefaultSingletonBeanRegistry#beforeSingletonCreation()java

    protected void beforeSingletonCreation(String beanName) {
       // inCreationCheckExclusions singletonsCurrentlyInCreation都是Set
       // 这里在做判断的同时,调用了Set的add()方法
       // 所以beanName被添加到了singletonsCurrentlyInCreation中
       if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
          throw new BeanCurrentlyInCreationException(beanName);
       }
    }
    

    3.7 AbstractAutowireCapableBeanFactory#doCreateBean()

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
    			throws BeanCreationException {
    
    	BeanWrapper instanceWrapper = null;
    	// 省略部分代码
    	if (instanceWrapper == null) {
    		// 重要步骤
    		// 在这里调用合适的构造方法生成实例,并将实例放在一个包装类中。这里就不详细展开了
    		instanceWrapper = createBeanInstance(beanName, mbd, args);
    	}
    	// 从包装类中将实例取出来
    	final Object bean = instanceWrapper.getWrappedInstance();
    	// 省略部分代码
    
    	// 这个boolean值的结果取决于->单例、allowCircularReferences为true(默认就是true)、对象正在创建中
    	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
    	if (earlySingletonExposure) {
    		// 省略部分代码
    		// 重要步骤
    		// 这个方法里会将bean添加到三级缓存中
    		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    	}
    
    
    	// Initialize the bean instance.
    	Object exposedObject = bean;
    	try {
    		// 重要步骤
    		// 给属性赋值
    		populateBean(beanName, mbd, instanceWrapper);
    		exposedObject = initializeBean(beanName, exposedObject, mbd);
    	} 
    
    	// 省略部分代码
    }
    

    这里有三段重要代码:

    • instanceWrapper = createBeanInstance(beanName, mbd, args);

      这个方法选择了合适的构造函数来构建实例。

    • addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

      在这个方法里将beanName和beanFactory放到了三级缓存中。详见3.8。

    • populateBean(beanName, mbd, instanceWrapper);

    3.8 AbstractAutowireCapableBeanFactory#createBeanInstance()

    3.9 DefaultSingletonBeanRegistry#addSingletonFactory()

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
       Assert.notNull(singletonFactory, "Singleton factory must not be null");
       synchronized (this.singletonObjects) {
          if (!this.singletonObjects.containsKey(beanName)) {
             // 如果 一级缓存中还没有这个beanName
             // 在三级缓存中做一下保存
             this.singletonFactories.put(beanName, singletonFactory);
             // 在二级缓存中移除
             this.earlySingletonObjects.remove(beanName);
             // 在registeredSingletons里添加
             this.registeredSingletons.add(beanName);
          }
       }
    }
    

    在这个方法里将beanName和beanFactory放到了三级缓存中。这时候,三级缓存里面已经有我要创建的dog对象(其实是有了创建这个对象的工厂)。

    3.10 AbstractAutowireCapableBeanFactory#populateBean()

    这个方法调用了AbstractAutowireCapableBeanFactory类applyPropertyValues()方法,applyPropertyValues()方法又调用了BeanDefinitionValueResolver类resolveReference()方法。最后又调用了doGetBean(),也就是3.3的方法。

    doGetBean()在这里是为了给dog对象创建它的Cat类成员变量。

    3.11 补充

    在DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)方法中,还调用了addSingleton(beanName, singletonObject);,这个方法会将bean添加到第一级缓存中,并从第二、第三级缓存中移除。当然这一步跟解决循环依赖没有关系。

    3.12 小总结

    总结一下整个过程:

    1. 根据field.xml里声明的bean的顺序,先进行dog对象的创建;
    2. 去三级缓存中获取dog对象,这时候当然还没有,所以返回null;
    3. 将beanName(也就是"dog")添加到了singletonsCurrentlyInCreation这个Set中;
    4. 通过一些列的判断条件,选择了合适的构造函数(这里是无参构造)来构建dog实例,此时的dog对象的成员变量都是null;
    5. 将beanName(也就是"dog")放入第三级缓存中;
    6. 给dog对象的属性赋值。这时候发现dog对象的成员变量需要注入一个Cat类的对象。这时候就去创建cat对象,创建的过程相当于第2步到第6步;
    7. 同第2步,去三级缓存中获取cat对象,这时候当然也没有,所以返回null;
    8. 同第3步,将beanName(也就是"cat")添加到了singletonsCurrentlyInCreation这个Set中;
    9. 同第4步,选择了合适的构造函数(这里是无参构造)来构建cat实例,此时的cat对象的成员变量都是null;
    10. 同第5步,将beanName(也就是"cat")放入第三级缓存中;
    11. 同第6步,给cat对象的属性赋值。这时候发现cat对象的成员变量需要注入一个Dog类的对象。执行第2步,发现第三级缓存中,已经可以获取到dog对象了,就将dog对象取出来,并保存到第二级缓存中,同时从第三级缓存中移除dog对象。现在可以将dog对象赋值到cat对象的成员变量中了。cat对象创建完成;
    12. 回到第6步,将cat对象赋值到dog对象的成员变量中,dog对象也创建完成。

    总的来说,Spring是依靠第三级缓存解决了循环依赖的问题,通过递归创建当前bean依赖的bean。

    本文由博客群发一文多发等运营工具平台 OpenWrite 发布

  • 相关阅读:
    如何在Android Studio中添加注释模板信息?
    Android Activity标签属性
    Android Activity全面解析
    Mac office ppt无法正常输入文字的问题解决方案
    将Android Studio默认布局ConstraintLayout切换成LinearLayout
    Java中Double保留后小数位的几种方法
    java文件传输接口
    纯JS编写打地鼠游戏
    JavaScript监听手机物理返回键的两种解决方法
    spring注解方式实现定时器
  • 原文地址:https://www.cnblogs.com/FatShallot/p/12784690.html
Copyright © 2011-2022 走看看