1. 首页
  2. >
  3. 编程技术
  4. >
  5. Java

Spring框架是怎么解决Bean之间的循环依赖的

在我们的开发中,会不可避免的遇到Bean之间循环依赖的,所谓循环依赖,就是两个或者两个以上的Bean互相持有对方,这样在程序运行调用中,会出现这种循环依赖的现象,假设是两个Bean,当程序调用Bean A时,Bean A中依赖Bean B,在Bean A中调用Bean B时,Bean B中又依赖了Bean A,这样就形成了循环依赖,如下图:

Spring框架是怎么解决Bean之间的循环依赖的

先从一个小例子来说明,使用Spring框架如果出现循环依赖,会正常运行吗?下例是在Spring Boot的基础上构建的。

代码结构如下:

Spring框架是怎么解决Bean之间的循环依赖的

程序访问入口是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:

Spring框架是怎么解决Bean之间的循环依赖的

启动没有问题,再来访问一下,浏览器输入:http://localhost:8080/hello

Spring框架是怎么解决Bean之间的循环依赖的

成功访问,再来看一下控制台,两个Bean也都已经实例化:

Spring框架是怎么解决Bean之间的循环依赖的

上面的简单例子可以说明Spring框架可以解决Bean之间循环依赖的,下面就来探究Spring是怎么做到的?

对于Spring中Bean的管理,下图一目了然:

Spring框架是怎么解决Bean之间的循环依赖的

先调用构造函数进行实例化,然后填充属性,再接着进行其他附加操作和初始化,正是这样的生命周期,才有了Spring的解决循环依赖,这样的解决机制是根据Spring框架内定义的三级缓存来实现的,也就是说:三级缓存解决了Bean之间的循环依赖。我们从源码中来说明。

先来看Spring中Bean工厂是怎么获取Bean的(AbstractBeanFactory中):

Spring框架是怎么解决Bean之间的循环依赖的

Spring框架是怎么解决Bean之间的循环依赖的

Spring框架是怎么解决Bean之间的循环依赖的

Spring框架是怎么解决Bean之间的循环依赖的

一级一级向下寻找,找出了前面提到的三级缓存,也就是三个Map集合类:

singletonObjects:第一级缓存,里面放置的是实例化好的单例对象;

earlySingletonObjects:第二级缓存,里面存放的是提前曝光的单例对象;

singletonFactories:第三级缓存,里面存放的是要被实例化的对象的对象工厂。

所以当一个Bean调用构造函数进行实例化后,即使属性还未填充,就可以通过三级缓存向外暴露依赖的引用值(所以循环依赖问题的解决也是基于Java的引用传递),这也说明了另外一点,基于构造函数的注入,如果有循环依赖,Spring是不能够解决的。还要说明一点,Spring默认的Bean Scope是单例的,而三级缓存中都包含singleton,可见是对于单例Bean之间的循环依赖的解决,Spring是通过三级缓存来实现的。源码是让我们知其然并且知其所以然的最好参考,所以多多阅读源码!