• 白话理解什么是Spring循环依赖


    我们知道Spring IOC是指的容器来负责创建Bean并负责处理Bean之间的依赖关系,比如有如下两个类A、B:

    @Component
    public class A
    	@Autowired
    	private B b;
    
    @Component
    public class B
    

    站在容器的角度,发现A依赖B,直接先创建B然后创建A,把B实例赋值给A的成员变量b,完事儿。

    但如果是下面一种循环依赖的情况,按照上面的逻辑就不可行了:

    循环依赖
    @Component
    public class A
    	@Autowired
    	private B b;
    	
    @Component
    public class B
    	@Autowired
    	private C c;
    	
    @Component
    public class C
    	@Autowired
    	private A a;
    

    A依赖B、B依赖C、C又依赖A

    如果你是容器,尼玛到底该先创建谁??

    解决思路

    创建一个类实例之前,先去一个全局的缓存Map里看下是否有这个类的实例,如果有则说明已经创建过、直接返回,如果没有则创建。
    创建类实例的对应的属性的实例、并赋值给成员变量。递归。
    创建A的实例,加入缓存Map,遍历A的属性,发现B需要创建,创建B的实例也加入缓存,遍历B的属性发现需要创建C,创建C的实例,遍历C的属性发现需要创建A的实例,但是A实例已经在缓存了、直接赋值。循环结束。

    下面按照上面的思路,写下代码模拟循环依赖的解决:

    package com.wangan.cyclicdependency;
    
    public class A {
    	public B b;
    }
    public class B {
    	public C c;
    }
    public class C {
    	public A a;
    }
    
    package com.wangan.cyclicdependency;
    
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 模拟简单循环依赖解决
     * */
    public class TestCyclicDependency {
    	
    	private static Map<String, Object> beanCache = new HashMap<>();
    	
    	public static void main(String[] args) throws Exception {
    		String[] beanDefinations = {"com.wangan.cyclicdependency.A", 
    					    "com.wangan.cyclicdependency.B", 
    					    "com.wangan.cyclicdependency.C"};
    		for(String beanName : beanDefinations) {
    			getBean(beanName);
    		}
    		
    		A a = (A) beanCache.get("com.wangan.cyclicdependency.A");	
    		B b = (B) beanCache.get("com.wangan.cyclicdependency.B");
    		C c = (C) beanCache.get("com.wangan.cyclicdependency.C");
    		System.out.println(a.b);
    		System.out.println(b.c);
    		System.out.println(c.a);
    		
    		System.out.println(a.b==b);
    		System.out.println(b.c==c);
    		System.out.println(c.a==a);
    	}
    	
    	public static Object getBean(String beanName) throws Exception {
    		if(beanCache.containsKey(beanName)) {
    			return beanCache.get(beanName);
    		}else {
    			Object bean = Class.forName(beanName).newInstance();
    			beanCache.put(beanName, bean);
    			Field[] fields = bean.getClass().getFields();
    			for(Field f : fields) {
    				String typeClassName = f.getType().getName();
    				f.setAccessible(true);
    				f.set(bean, getBean(typeClassName));
    			}
    			return bean;
    		}
    	}
    	
    }
    
    Spring中的循环依赖问题

    先对于我们上面的简单例子,Spring要面对的问题要复杂一些,即要实现AOP,相对于我们上面使用了1个Map来作为缓存来暂存Bean,Spring使用了3个Map形成所谓的“三级缓存”。

    DefaultSingletonBeanRegistry.java,继承关系是DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory extends AbstractBeanFactory extends FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry (吐槽一下,所以说Spring的源代码为什么难读,瞅瞅这继承的关系有多深)

    /** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    /** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    /** Cache of early singleton objects: bean name to bean instance. */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    • singletonObjects 是1级缓存,就是所谓的单例池,存放经过代理的且经过初始化的完整的Bean。
    • earlySingletonObjects 是2级缓存,存放实例化之后、且经过代理的Bean,但是还没有初始化(也就是属性还没填充)。
    • singletonFactories 是3级缓存,存放用实例化后的Bean对象封装成的工厂对象,如果有AOP则这个工厂对象getObject直接返回原实例化对象、如果启用了AOP那么这个工厂对象返回的就是加了AOP切面的动态代理对象。

    下面我们还是用代码模拟一下,以加深理解:

    互相依赖的两个类,A和B:

    import lombok.Data;
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    @Data
    public class A {
    	
    	@Autowired
    	private B b;
    	
    	public A() {
    		log.info("A初始化");
    	}
    	
    	public void helloA() {
    		log.info("hello A");
    	}
    }
    
    @Slf4j
    @Data
    public class B {
    	
    	@Autowired
    	private A a;
    	
    	public B() {
    		log.info("B初始化");
    	}
    	
    	public void helloB() {
    		log.info("hello B");
    	}
    }
    

    DefaultSingletonBeanRegistry

    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;
    
    import org.springframework.beans.factory.ObjectFactory;
    import org.springframework.util.Assert;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class DefaultSingletonBeanRegistry {
    	//1级缓存
    	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    	//2级缓存
    	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    	//3级缓存
    	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    	private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
    
    	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();
    						this.earlySingletonObjects.put(beanName, singletonObject);
    						this.singletonFactories.remove(beanName);
    					}
    				}
    			}
    		}
    		return singletonObject;
    	}
    
    	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) {
    
    				this.singletonsCurrentlyInCreation.add(beanName);
    				boolean newSingleton = false;
    
    				singletonObject = singletonFactory.getObject();
    				newSingleton = true;
    
    				this.singletonsCurrentlyInCreation.remove(beanName);
    
    				if (newSingleton) {
    					addSingleton(beanName, singletonObject);
    				}
    			}
    			return singletonObject;
    		}
    	}
    
    	protected void addSingleton(String beanName, Object singletonObject) {
    		synchronized (this.singletonObjects) {
    			this.singletonObjects.put(beanName, singletonObject);
    			this.singletonFactories.remove(beanName);
    			this.earlySingletonObjects.remove(beanName);
    			//this.registeredSingletons.add(beanName);
    		}
    	}
    
    	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);
    			}
    		}
    	}
    
    	protected void removeSingleton(String beanName) {
    		synchronized (this.singletonObjects) {
    			this.singletonObjects.remove(beanName);
    			this.singletonFactories.remove(beanName);
    			this.earlySingletonObjects.remove(beanName);
    			//this.registeredSingletons.remove(beanName);
    		}
    	}
    
    	public boolean isSingletonCurrentlyInCreation(String beanName) {
    		return this.singletonsCurrentlyInCreation.contains(beanName);
    	}
    }
    

    BeanFactory,继承BeanRegistry:

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.util.Arrays;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    
    public class DefaultListenableBeanFactory extends DefaultSingletonBeanRegistry{
    	private final Map<String, RootBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    	
    	public void registerBeanDefinition(String beanName, RootBeanDefinition beanDefinition) {
    		this.beanDefinitionMap.put(beanName, beanDefinition);
    	}
    	
    	public Object getBean(String beanName) {
    		return doGetBean(beanName);
    	}
    	
    	private Object doGetBean(String beanName) {
    		Object bean;
    		Object sharedInstance = getSingleton (beanName, true);
    		if(sharedInstance != null) {
    			return sharedInstance;
    		}else {
    			RootBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
    			bean = getSingleton(beanName, () -> {
    				return doCreateBean(beanName, beanDefinition);
    			});
    		}
    		return bean;
    	}
    	
    	//创建Bean
    	private Object doCreateBean(String beanName, RootBeanDefinition beanDefinition) {
    		Object bean = createBeanInstance(beanName, beanDefinition);
    		
    		//如果beanName正在创建中,而本次doCreateBean又创建,说明出现了循环依赖现象,则把它放入3级缓存里
    		boolean earlySingletonExposure = this.isSingletonCurrentlyInCreation(beanName);
    		if(earlySingletonExposure) {
    			this.addSingletonFactory(beanName, ()->bean);
    		}
    		
    		Object exposedObject = bean;
    		
    		//属性赋值
    		populateBean(beanName, beanDefinition, bean);
    		
    		if(earlySingletonExposure) {
    			Object earlySingletonReference = getSingleton (beanName, false);
    			if(earlySingletonReference != null)
    				exposedObject = earlySingletonReference;
    		}
    		return exposedObject;
    	}
    	
    	//实例化
    	private Object createBeanInstance(String beanName, RootBeanDefinition beanDefinition) {
    		Constructor<Object> constructor;
    		try {
    			constructor = (Constructor<Object>) beanDefinition.getBeanClass().getDeclaredConstructor();
    			constructor.setAccessible(true);
    			return constructor.newInstance();
    		} catch (Exception e) {
    			e.printStackTrace();
    		} 
    		return null;
    	}
    	
    	//属性填充
    	private void populateBean(String beanName, RootBeanDefinition beanDefinition, Object beanInstance) {
    		Field[] fields = beanDefinition.getBeanClass().getDeclaredFields();
    		Arrays.stream(fields).forEach(field -> {
    			Autowired autowired = field.getAnnotation(Autowired.class);
    			if(null != autowired) {
    				Object beanField = this.getBean(field.getName());
    				field.setAccessible(true);
    				try {
    					field.set(beanInstance, beanField);
    				} catch (IllegalArgumentException e) {
    					e.printStackTrace();
    				} catch (IllegalAccessException e) {
    					e.printStackTrace();
    				}
    			}
    		});
    	}
    }
    

    测试:

    public class CyclicDependencyTest {
    	public static void main(String[] args) {
    		RootBeanDefinition beanDefinitionA = new RootBeanDefinition();
    		beanDefinitionA.setBeanClass(A.class);
    		RootBeanDefinition beanDefinitionB = new RootBeanDefinition();
    		beanDefinitionB.setBeanClass(B.class);
    		
    		DefaultListenableBeanFactory beanFactory = new DefaultListenableBeanFactory();
    		beanFactory.registerBeanDefinition("a", beanDefinitionA);
    		beanFactory.registerBeanDefinition("b", beanDefinitionB);
    		
    		A a = (A) beanFactory.getBean("a");
    		a.getB().helloB();
    		
    		B b = (B) beanFactory.getBean("b");
    		b.getA().helloA();
    	}
    }
    

    输出:
    [2021-11-28 12:09:32] [ INFO ] [main] [codeline:16] - A初始化
    [2021-11-28 12:09:32] [ INFO ] [main] [codeline:16] - B初始化
    [2021-11-28 12:09:32] [ INFO ] [main] [codeline:20] - hello B
    [2021-11-28 12:09:32] [ INFO ] [main] [codeline:20] - hello A

    参考

    https://mp.weixin.qq.com/s/kS0K5P4FdF3v-fiIjGIvvQ 感谢这个aobing小兄弟,闻道有先后,从他写的和整理的材料里理顺了不少思路

  • 相关阅读:
    idea工具如何在修改jsp后不用一直重启idea
    解决端口被占用问题
    tomcat端口强制关闭
    tomcat部署方式之三(war包,也是最重要的)
    tomcat部署方式之二
    tomcat的配置方式一
    在启动tomcat时出现java_home未配置的问题(闪退)
    mysql出现“mysql不是内部或外部命令也不是可运行”
    staruml下载
    用java语言实现一个观察者模式
  • 原文地址:https://www.cnblogs.com/lyhero11/p/15603602.html
Copyright © 2020-2023  润新知