在我们的开发中,会不可避免的遇到Bean之间循环依赖的,所谓循环依赖,就是两个或者两个以上的Bean互相持有对方,这样在程序运行调用中,会出现这种循环依赖的现象,假设是两个Bean,当程序调用Bean A时,Bean A中依赖Bean B,在Bean A中调用Bean B时,Bean B中又依赖了Bean A,这样就形成了循环依赖,如下图:
先从一个小例子来说明,使用Spring框架如果出现循环依赖,会正常运行吗?下例是在Spring Boot的基础上构建的。
代码结构如下:
程序访问入口是HelloController,它里面调用了HelloService1:
package com.pig.employee.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMApping; import org.springframework.web.bind.annotation.RestController; import com.pig.employee.service1.HelloService1; @RestController public class HelloController { @Autowired HelloService1 helloService1; @RequestMapping("/hello") public String sayHello() { return helloService1.say1(); } }
看一下HelloService1对应的实现类:
package com.pig.employee.service1.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.pig.employee.service1.HelloService1; import com.pig.employee.service2.HelloService2; @Service("helloService1") public class HelloService1Impl implements HelloService1 { @Autowired private HelloService2 helloService2; @Override public String say1() { System.out.println(helloService2.toString()); return helloService2.say2(); } }
实现类中依赖了HelloService2,再来看一下HelloService2的实现类:
package com.pig.employee.service2.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.pig.employee.service1.HelloService1; import com.pig.employee.service2.HelloService2; @Service("helloService2") public class HelloService2Impl implements HelloService2 { @Autowired private HelloService1 helloService1; @Override public String say2() { System.out.println(helloService1.toString()); return "helloService2 say hello"; } }
HelloService2的实现类中又依赖了HelloService1,这样就形成了循环依赖,依托于Spring框架,这样的循环依赖能运行成功吗?废话不多说,直接运行不就出答案了,启动EmployeeApplication:
启动没有问题,再来访问一下,浏览器输入:http://localhost:8080/hello
成功访问,再来看一下控制台,两个Bean也都已经实例化:
上面的简单例子可以说明Spring框架可以解决Bean之间循环依赖的,下面就来探究Spring是怎么做到的?
对于Spring中Bean的管理,下图一目了然:
先调用构造函数进行实例化,然后填充属性,再接着进行其他附加操作和初始化,正是这样的生命周期,才有了Spring的解决循环依赖,这样的解决机制是根据Spring框架内定义的三级缓存来实现的,也就是说:三级缓存解决了Bean之间的循环依赖。我们从源码中来说明。
先来看Spring中Bean工厂是怎么获取Bean的(AbstractBeanFactory中):
一级一级向下寻找,找出了前面提到的三级缓存,也就是三个Map集合类:
singletonObjects:第一级缓存,里面放置的是实例化好的单例对象;
earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;
singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂。
所以当一个Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外暴露依赖的引用值(所以循环依赖问题的解决也是基于JAVA的引用传递),这也说明了另外一点,基于构造函数的注入,如果有循环依赖,Spring是不能够解决的。还要说明一点,Spring默认的Bean Scope是单例的,而三级缓存中都包含singleton,可见是对于单例Bean之间的循环依赖的解决,Spring是通过三级缓存来实现的。源码是让我们知其然并且知其所以然的最好参考,所以多多阅读源码!