本文将从什么是循环定义、循环定义的分类、循环定义的实现、循环定义的优缺点以及循环定义的应用等方面进行详细阐述。
一、什么是循环定义
循环定义是指定义A依赖于B,同时B也依赖于A的一种定义结构。
举例来说,我们定义两个类A和B,其中A依赖于B,B也依赖于A
<pre>
class A{
private B b;
public A(){
b = new B(this);
}
}
class B{
private A a;
public B(A a){
this.a = a;
}
}
</pre>
二、循环定义的分类
循环定义可以分为显式循环定义和隐式循环定义两种类型。
(一)显式循环定义
显式循环定义是指A和B彼此之间都明确地依赖于对方
<pre>
class A{
private B b;
public A(){
b = new B(this);
}
}
class B{
private A a;
public B(A a){
this.a = a;
}
}
</pre>
(二)隐式循环定义
隐式循环定义是指A和B使用的对象或者初始化函数可能会间接地依赖于彼此
<pre>
class A{
private B b;
public A(){
b = getBInstance();
}
public B getBInstance(){
return new B();
}
}
class B{
private A a;
public B(){
a = getAInstance();
}
public A getAInstance(){
return new A();
}
}
</pre>
三、循环定义的实现
在使用循环定义时,需要注意以下两点:
- 确保在定义之前所有必需的元素都已经被提前定义
- 为避免无限递归,需要使用延迟初始化(Lazy Initialization)或者缓存机制
四、循环定义的优缺点
(一)优点
- 使代码更具有灵活性和可扩展性
- 提高了程序的可读性和可维护性
(二)缺点
- 会导致程序的性能下降
- 难以调试,因为代码非常复杂且难以理解
- 需要谨慎地考虑所有的依赖关系,否则可能会引起一系列莫名其妙的问题
- 可能会导致死锁或者内存泄露等问题
五、循环定义的应用
循环定义可以在一些框架或是具有复杂依赖关系的程序中发挥重要作用。
比如,在Spring框架中,Bean之间的依赖关系可能会非常复杂,而且很可能会出现循环依赖的情况。为了解决这个问题,Spring使用了两个辅助类:DefaultSingletonBeanRegistry和DependencyDescriptor,通过特定的实现方式,使得在循环依赖的情况下,Spring可以正确地实例化Bean。
<pre>
public abstract class AbstractBeanFactory implements BeanFactory {
protected Map singletonObjects = new ConcurrentHashMap();
protected Map earlySingletonObjects = new HashMap();
protected Set singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap(16));
protected Map<String, ObjectFactory> singletonFactories =
new HashMap<String, ObjectFactory>();
protected Map beanDefinitionMap = new ConcurrentHashMap(64);
protected Map<Class, Object> factoryBeanInstanceCache = new ConcurrentHashMap<Class, Object>(16);
protected final Map mergedBeanDefinitionMap =
new ConcurrentHashMap(64);
...
}
public class DependencyDescriptor extends InjectionPoint {
private String[] aliases = new String[0];
private Class containingClass;
private boolean required = true;
private boolean eager = false;
private ResolvableType type = ResolvableType.NONE;
public DependencyDescriptor(MethodParameter methodParameter, boolean eager) {
super(methodParameter);
this.eager = eager;
this.containingClass = methodParameter.getContainingClass();
}
public DependencyDescriptor(Field field, boolean required) {
super(field);
this.required = required;
this.containingClass = field.getDeclaringClass();
}
public DependencyDescriptor(DependencyDescriptor original) {
super(original);
this.type = original.type;
this.required = original.required;
this.containingClass = original.containingClass;
}
...
}
</pre>