Appearance
Spring 循环依赖 
什么是循环依赖 
当 A 依赖 B,而 B 又依赖 A 时,它们之间就发生了循环依赖。
A -> B -> A
出现循环依赖的两种情况 
构造方法注入(Constructor Injection) 
同是构造方法注入时发生的循环依赖是无法被解决的,因为它们的实例化需要依赖对方的注入。
java
static class A {
    private B b;
    public A(B b) {
        super();
        this.b = b;
    }
}
static class B {
    private A a;
    public B(A a) {
        super();
        this.a = a;
    }
}设值方法注入(Setter Injection) 
设值方法注入时发生的循环依赖是能够被解决的,因为它们的实例化不需要依赖对方的注入。
java
static class A {
    private B b;
    public void setB(B b) {
        this.b = b;
    }
}
static class B {
    private A a;
    public void setA(A a) {
        this.a = a;
    }
}出现循环依赖的处理方式 
我们都知道 Spring 等框架是通过反射的方式创建 Bean 的。而且为了保证性能,一般都会采用单例模式创建 Bean。下面就来看看在使用反射创建 Bean 的时候是如何解决循环依赖的。
设值方法注入(Setter Injection)时循环依赖的处理方式 
java
package study.helloworld.something4j._2022._01._14;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class CircularDependencyWithSetter {
	public static void main(String[] args) {
		Stream.of(A.class, B.class)
				.forEach(CircularDependencyWithSetter::getSingleton);
		System.out.println("A instance is a singleton: "
				+ (getSingleton(A.class) == getSingleton(B.class).getA()));
		System.out.println("B instance is a singleton: "
				+ (getSingleton(B.class) == getSingleton(A.class).getB()));
	}
	private static final Map<String, Object> SINGLETON_OBJECT_MAP = new HashMap<String, Object>();
	@SuppressWarnings("unchecked")
	static <T> T getSingleton(Class<T> beanClass) {
		String beanName = beanClass.getSimpleName();
		Object object = SINGLETON_OBJECT_MAP.get(beanName);
		if (object == null) {
			try {
				object = beanClass.getDeclaredConstructor().newInstance();
				SINGLETON_OBJECT_MAP.put(beanName, object);
				Field[] fields = beanClass.getDeclaredFields();
				for (Field field : fields) {
					field.setAccessible(true);
					Class<?> fieldClass = field.getType();
					field.set(object, getSingleton(fieldClass));
				}
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
		}
		return (T) object;
	}
	static class A {
		private B b;
		public B getB() {
			return b;
		}
		public void setB(B b) {
			this.b = b;
		}
	}
	static class B {
		private A a;
		public A getA() {
			return a;
		}
		public void setA(A a) {
			this.a = a;
		}
	}
}运行结果:
java
A instance is a singleton: true
B instance is a singleton: true可以看到,我们只用了一个 Map 就解决了设值方法注入时循环依赖的问题。
构造方法注入(Constructor Injection)时循环依赖的处理方式 
我们已经知道同是构造方法注入时发生的循环依赖是无法被解决的。为了验证,考虑下面代码:
java
package study.helloworld.something4j._2022._01._14;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class CircularDependencyWithConstructor {
	public static void main(String[] args) {
		Stream.of(A.class, B.class)
				.forEach(CircularDependencyWithSetter::getSingleton);
		System.out.println("A instance is a singleton: "
				+ (getSingleton(A.class) == getSingleton(B.class).getA()));
		System.out.println("B instance is a singleton: "
				+ (getSingleton(B.class) == getSingleton(A.class).getB()));
	}
	private static final Map<String, Object> SINGLETON_OBJECT_MAP = new HashMap<String, Object>();
	@SuppressWarnings("unchecked")
	static <T> T getSingleton(Class<T> beanClass) {
		String beanName = beanClass.getSimpleName();
		Object object = SINGLETON_OBJECT_MAP.get(beanName);
		if (object == null) {
			try {
				Constructor<?> constructor = beanClass.getConstructors()[0];
				Class<?>[] parameterTypes = constructor.getParameterTypes();
				Object[] parameterObjects = new Object[parameterTypes.length];
				for (int i = 0; i < parameterTypes.length; i++) {
					parameterObjects[i] = getSingleton(parameterTypes[i]);
				}
				constructor.newInstance(parameterObjects);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return (T) object;
	}
	static class A {
		private B b;
		public A(B b) {
			super();
			this.b = b;
		}
		public B getB() {
			return b;
		}
	}
	static class B {
		private A a;
		public B(A a) {
			super();
			this.a = a;
		}
		public A getA() {
			return a;
		}
	}
}这会得到一个 java.lang.StackOverflowError 错误。
为了及时发现此类问题,可以使用一个 Set 来存放还在构造中的 Bean 的 beanName。如果 beanName 已经存在于 Set,那么直接抛出 java.lang.RuntimeException。
java
package study.helloworld.something4j._2022._01._14;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
public class CircularDependencyWithConstructor {
	public static void main(String[] args) {
		Stream.of(A.class, B.class)
				.forEach(CircularDependencyWithSetter::getSingleton);
		System.out.println("A instance is a singleton: "
				+ (getSingleton(A.class) == getSingleton(B.class).getA()));
		System.out.println("B instance is a singleton: "
				+ (getSingleton(B.class) == getSingleton(A.class).getB()));
	}
	private static final Map<String, Object> SINGLETON_OBJECT_MAP = new HashMap<String, Object>();
	private static final Set<String> SINGLETON_IN_CREATION_SET = new HashSet<String>();
	@SuppressWarnings("unchecked")
	static <T> T getSingleton(Class<T> beanClass) {
		String beanName = beanClass.getSimpleName();
		Object object = SINGLETON_OBJECT_MAP.get(beanName);
		if (object == null) {
			if (!SINGLETON_IN_CREATION_SET.add(beanName)) {
				throw new RuntimeException("Error creating bean with name '"
						+ beanName + "': "
						+ "Requested bean is currently in creation: Is there an unresolvable circular reference?");
			}
			try {
				Constructor<?> constructor = beanClass.getConstructors()[0];
				Class<?>[] parameterTypes = constructor.getParameterTypes();
				Object[] parameterObjects = new Object[parameterTypes.length];
				for (int i = 0; i < parameterTypes.length; i++) {
					parameterObjects[i] = getSingleton(parameterTypes[i]);
				}
				constructor.newInstance(parameterObjects);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return (T) object;
	}
	static class A {
		private B b;
		public A(B b) {
			super();
			this.b = b;
		}
		public B getB() {
			return b;
		}
	}
	static class B {
		private A a;
		public B(A a) {
			super();
			this.a = a;
		}
		public A getA() {
			return a;
		}
	}
}