1

循环依赖

Spring著名的技术之一就是IOC,让容器帮我们管理Bean

所谓循环依赖,就像下图描述的一样,CatService中依赖DogServiceDogService中又依赖CatService,形成一个循环。

clipboard.png

过去,我们只知道Spring会帮助我们处理Bean之间的依赖关系的,所以也没去管太多。最近由于某些Bean在构造函数中进行注入,发现循环依赖出现问题了,我们有必要去研究研究这个底层的原理。

Autowired标注于属性之上

这里只是表示思想,直接就用类来演示了,未写接口,实际开发中需要面向接口,降低耦合。

@Service
public class CatService {
    @Autowired
    private DogService dogService;
}

@Service
public class DogService {
    @Autowired
    private CatService catService;
}

创建CatService

其实,容器就是替我们做了创建对象的这个操作。无论底层是怎么写的,第一步都是new对象。

假设容器先扫描的CatService,然后容器会调用其构造函数创建一个CatService对象。

clipboard.png

clipboard.png

容器中有两个ConcurrentHashMap 用于解决循环依赖问题,ObjectHashMapFactoryHashMap,分别用于存放完美的和不完美的对象。

clipboard.png

创建完之后,就将这个不完美的catService放在FactoryHashMap中做缓存。

clipboard.png

设置需要的属性

Spring一扫描,发现CatService中需要一个DogService

然后就开始找,ObjectHashMap中有没有完美的DogService啊?有就直接用,没有接着去FactoryHashMap中找。如果有就用,虽然不完美,起码比没有好。

如果这两个里面都没有,那就没招了,再去new一个吧。

创建DogService

CatService的创建过程一致,先new一个不完美的DogService出来。

clipboard.png

同样的,也将dogService放入了缓存中。

clipboard.png

设置需要的属性

DogService中需要CatService

然后去找CatService,没有完美的,FactoryHashMap中有一个不完美但是能用的。

dogService注入CatService

所以,一个DogService就创建完成了。

完美

上面创建的DogService是在为catService设置属性时调用的,调用之后,DogService便存在。

然后再为catService注入刚刚创建出来的dogService

这样,相互就注入完成了,应为所持有的都是引用,所以一改变都跟着改变了,原来不完美的CatServiceDogService在最后一步完成之后,也变得完美了。

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也是不能循环依赖的。

总结

  1. 最好将Autowired注解标注在属性上。
  2. 构造函数方式与多例Bean是不能解决循环依赖的问题的。

张喜硕
2.1k 声望423 粉丝

浅梦辄止,书墨未浓。