循环依赖
Spring
著名的技术之一就是IOC
,让容器帮我们管理Bean
。
所谓循环依赖,就像下图描述的一样,CatService
中依赖DogService
,DogService
中又依赖CatService
,形成一个循环。
过去,我们只知道Spring
会帮助我们处理Bean
之间的依赖关系的,所以也没去管太多。最近由于某些Bean
在构造函数中进行注入,发现循环依赖出现问题了,我们有必要去研究研究这个底层的原理。
Autowired标注于属性之上
这里只是表示思想,直接就用类来演示了,未写接口,实际开发中需要面向接口,降低耦合。
@Service
public class CatService {
@Autowired
private DogService dogService;
}
@Service
public class DogService {
@Autowired
private CatService catService;
}
创建CatService
其实,容器就是替我们做了创建对象的这个操作。无论底层是怎么写的,第一步都是new
对象。
假设容器先扫描的CatService
,然后容器会调用其构造函数创建一个CatService
对象。
容器中有两个ConcurrentHashMap
用于解决循环依赖问题,ObjectHashMap
和FactoryHashMap
,分别用于存放完美的和不完美的对象。
创建完之后,就将这个不完美的catService
放在FactoryHashMap
中做缓存。
设置需要的属性
Spring
一扫描,发现CatService
中需要一个DogService
。
然后就开始找,ObjectHashMap
中有没有完美的DogService
啊?有就直接用,没有接着去FactoryHashMap
中找。如果有就用,虽然不完美,起码比没有好。
如果这两个里面都没有,那就没招了,再去new
一个吧。
创建DogService
与CatService
的创建过程一致,先new
一个不完美的DogService
出来。
同样的,也将dogService
放入了缓存中。
设置需要的属性
DogService
中需要CatService
。
然后去找CatService
,没有完美的,FactoryHashMap
中有一个不完美但是能用的。
为dogService
注入CatService
。
所以,一个DogService
就创建完成了。
完美
上面创建的DogService
是在为catService
设置属性时调用的,调用之后,DogService
便存在。
然后再为catService
注入刚刚创建出来的dogService
。
这样,相互就注入完成了,应为所持有的都是引用,所以一改变都跟着改变了,原来不完美的CatService
与DogService
在最后一步完成之后,也变得完美了。
Autowired标注于构造函数之上
我们理解了循环依赖的执行过程,所以如果像下面这样,就会出错。
@Service
public class CatService {
private final DogService dogService;
@Autowired
public CatService(DogService dogService) {
this.dogService = dogService;
}
}
@Service
public class DogService {
private final CatService catService;
@Autowired
public DogService(CatService catService) {
this.catService = catService;
}
}
先创建CatService
,其构造函数中需要DogService
,然后又去创建DogService
。
发现在DogService
的构造函数中又需要CatService
,又去创建CatService
,因为当前正在创建CatService
,再去创建就会报异常:BeanCurrentlyInCreationException
。
多例Bean
对于默认的单例Bean
,可以通过上面的缓存方式解决循环依赖。
Spring
容器是不会对多例的Bean
进行缓存的,所以多例Bean
也是不能循环依赖的。
总结
- 最好将
Autowired
注解标注在属性上。 - 构造函数方式与多例
Bean
是不能解决循环依赖的问题的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。