什么是循环依赖
循环依赖就是多个Bean之间存在相互依赖,形成一个闭环,如下,PostService和UserService之间就存在相互依赖,这个依赖并不是方法 之间的依赖,而是Bean与Bean之间的依赖。
产生循环依赖的情景
1.构造注入
构造注入不能解决,无论是单例还是多例。
@Component
public class PostService {
private UserService userService;
@Autowired
public PostService(UserService userService) {
this.userService = userService;
}
}
@Component
public class UserService {
private PostService postService;
@Autowired
public UserService(PostService postService) {
this.postService = postService;
}
}
2.Bean的作用域为多例
Bean的作用域为多例,也无法解决循环依赖,无论是setter注入,构造注入,还是filed注入。
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PostService {
@Autowired
private UserService userService;
}
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserService {
@Autowired
private PostService postService;
}
判断循环依赖是否存在
Spring检查循环依赖是在创建Bean的时候检查存放Bean的集合中查看Bean是否已经存在,如果已经存在,则证明Bean已经创建过,就会抛出循环依赖的异常 BeanCurrentlyInCreationException
,看一下检查的源码。
在DefaultSingletonBeanRegistry
类下有一个beforeSingletonCreation
方法,就是检查是否已经存在Bean
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
看一下两个集合
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
singletonsCurrentlyInCreation
是正在创建的单例Bean Set集合,inCreationCheckExclusions
是已经创建的Bean Set集合, 如果inCreationCheckExclusions中已经存在了Bean,那么就会抛出循环依赖异常,如果不存在,就会继续创建Bean。
Spring怎么解决循环依赖
Spring为了解决循环依赖问题,引入了三级缓存,如果了解Bean的生命周期,从Bean的生命周期可以知道Bean在实例化的时候会通过Bean的构造函数来实例化Bean( 这也是为什么使用构造函数无法解决循环依赖问题的原因
)和进行属性填充,所以就要在这一步之前对Bean进行一些操作,
三级缓存
/**
*一级缓存
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
*二级缓存
*/
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/**
*三级缓存
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
Spring解决循环依赖流程
Spring要创建PostService这个Bean,会进入AbstractBeanFactory类的doGetBean方法
//获取单例bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
通过getSingleton
方法获取单例Bean,首先从一级缓存singletonObjects中获取Bean,如果不存在,再从二级缓存earlySingletonObjects中获取Bean, 二级缓存中不存在,再从三级缓存singletonFactories中获取,如果三级缓存中也没有,就返回null,显然,此时缓存中都没有PostService这个Bean。
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
如果一级二级缓存中都没有获取到,那么就会执行下面代码。
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
然后就去创建Bean,创建完Bean以后,将Bean放到三级缓存中去,这里创建的Bean叫做可以叫做早期Bean
,是不完整的,没有经过属性赋值和实例化。
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
此时,PostService
(早期Bean)就被放到了三级缓存singletonFactories
中,于是PostService进行属性赋值,发现PostService
中依赖了UserService
, 此时会重复上面的过程去创建 UserService,将UserService放到三级缓存中,而UserService中依赖PostService,从一级二级缓存中没找到PostService,从三级缓存中找到了PostService,然后将PostService从三级缓存中移除,放入 二级缓存earlySingletonObjects中。
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
找到了PostService放入二级缓存并返回,此时UserService完成了对PostService的注入,然后UserService继续往下创建,创建完成后返回,然后将PostService从二级缓存移到一级缓存。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//加入一级缓存
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
//从二级缓存中移除
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
此时,PostService完成了对UserService的注入。
上面我们说了构造函数Spring不能解决循环依赖问题,Bean为多例也无法解决循环依赖问题,下面来说一下问什么。
构造函数
因为Bean在进行实例化的时候会调用Bean的构造函数来进行实例化,而使用构造函数注入,显然bean在实例化的时候直接使用构造函数进行Bean的实例化了, 所以这是Spring无法解决的。
Bean的作用域为多例
因为三级缓存是针对于单例,因为单例在整个服务中只存在一个,所以能够实现在一级二级和三级缓存之间的转移,而多例则每次创建都 都会创建一个实例,这样就会存在多个实例,无法在多级缓存之间存储与转移。
怎么避免循环依赖
1.使用@Autowired的方式进行Bean的注入。
2.使用setter方式进行注入。
3.如果存在循环依赖,那么不用构造注入。
4.如果是Bean为多例,要注意是否存在循环依赖。
今天的分享就到这里,感谢你的观看,下期间
原文始发于微信公众号(刘牌):Spring篇之循环依赖
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/150867.html