网上的说法是:构造器注入方式出现循环依赖时会在启动的时候抛出异常,而属性注入的方式出现循环依赖时会在调用的时候抛出异常,但是给出的代码执行起来跟理论结果不一致,直到某天看到一篇文章提到跟单例和原型有关。
@Component
public class ClassA {
@Autowired
private ClassBclassb;
private Stringname ="classA的名字";
public StringgetName() { return name;}
}
@Component
public class ClassB {
@Autowired
private ClassAclassa;
private Stringname ="classB的名字";
public StringgetName() {return name;}
}
@Autowired
private ClassA classA;
@Test
public void testRelay(){
System.out.println(classA.getName());
System.out.println("=================over");
}
这种方式,理论上打印时会报错,但是实际上会执行成功,并且 classA有值
如果在ClassA和ClassB上加上
@Scope(value = "prototype")
那么启动就会报错,发生了循环依赖,但是只在ClassA或者ClassB上加以上注解不会出现问题
结论:
属性注入确实可能产生循环依赖,但是本质是单例多例的问题,当创建ClassA后,对属性注入值时,发现需要ClassB,所以去构建ClassB,由于ClassB又依赖ClassA。
假如是单例模式,那么此时ClassA已经有了(即使没有赋值,但是bean已经存在了),所以ClassB的ClassA字段直接赋值然后返回,ClassA初始化完成,由于对象是地址值引用,所以ClassB的ClassA字段也更新成了最新的ClassA的bean(同一地址)。
但是如果都是多例模式且,ClassB需要ClassA,但是ClassA又是多例模式,则ClassB又去创建ClassA,然后ClassA又去创建ClassB,循环出现。
如果只有一个是多例,一个单例(spring默认是单例模式),假如是ClassA单例,ClassB多例,ClassA注入ClassB时创建ClassB,ClassB注入ClassA时,ClassA有了,直接注入,然后返回,不会出现循环依赖。