spring循环依赖策略解析

作者:atheva 时间:2022-09-13 13:49:10 

循环依赖

所谓循环依赖就是多个Bean之间依赖关系形成一个闭环,例如A->B->C->...->A 这种情况,当然,最简单的循环依赖就是2个Bean之间互相依赖:A->B(A依赖B), B->A(B依赖A) 。在Spring中,如果A->B,那么在创建A的过程中会去创建B,在创建B(或B的依赖)的过程中又发现B->A,这个时候就出现了循环依赖的现象。

循环依赖的解决

spring中的循环依赖只有当

1.Bean是单例,
2.通过属性注入的情况

这两个条件满足的情况下是没问题的。但是如果是通过构造器依赖,或者不是单例模式的情况下循环依赖就会抛出异常BeanCurrentlyInCreationException。下面从代码层面上解析一下为什么。

Prototype的循环依赖问题

为什么最先介绍Prototype的循环依赖呢,因为可以顺便介绍在Spring中创建Bean的流程核心流程:在AbstractFoctory的doGetBean的方法。这个方法很长,这里只写出核心逻辑,并在注解上注明了个人理解:


protected <T> T doGetBean(
 final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
 throws BeansException {

final String beanName = transformedBeanName(name);
Object bean;

//尝试获取单例对象,因为spring大部分的bean都是单例的,所以这里先尝试能否获取。
registered singletons.
Object sharedInstance = getSingleton(beanName);
//单例存在的情况下,那么beanName返回的肯定是单例类,但是这里还需要判断是不是FactoryBean
if (sharedInstance != null && args == null) {
 ...
 //FactoryBean应该返回getObject()对象
 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

else {
 //走到这里,有可能beanName是单例模式,但之前并没有实例化,或者是Prototype类型。
 //首先判断不是循环依赖,这里的循环依赖指的是Prototype类型
 if (isPrototypeCurrentlyInCreation(beanName)) {
  throw new BeanCurrentlyInCreationException(beanName);
 }

try {
  final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  // 如果是单例,则创建单例模式
  if (mbd.isSingleton()) {
   // !!!这里是解决单例循环依赖的关键,后面再分析
   sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    @Override
    public Object getObject() throws BeansException {
     try {
      return createBean(beanName, mbd, args);
     }
     catch (BeansException ex) {
      throw ex;
     }
    }
   });
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  }

else if (mbd.isPrototype()) {
   // 原型模式,则创建一个新对象.
   Object prototypeInstance = null;
   try {
    /*这里是Prototype循环依赖的问题,会记录在map中beanName,
    如果在解决当前Bean的依赖过程中还依赖当前Bean,
    则说明了出现了循环依赖
    */
      beforePrototypeCreation(beanName);
      prototypeInstance = createBean(beanName, mbd, args);
   }
   finally {
    //对应beforePrototypeCreation(),从map中移除
      afterPrototypeCreation(beanName);
   }
   bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
  }
  ...
 }
}

...
return (T) bean;
}

可以看出,该流程中就考虑了Prototype的循环依赖的问题,只要在创建Prototype的Bean中出现循环依赖那么就抛出异常。但是在singleton的情况下,则通过另外的方式来解决。

Singleton的循环依赖之构造注入

在上面介绍中,出现了一个很关键的地方:


sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
 try {
  return createBean(beanName, mbd, args);
 }
 catch (BeansException ex) {
  throw ex;
 }
}
});

这个getSingleton涉及到了ObjectFactory这个接口类,这个接口的功能和FactoryBean类似,但是主要是用来解决循环依赖的。在初始化过程同决定返回的Singleton对象是。关于单例的对象的创建,又要介绍一下DefaultSingletonBeanRegistry这个类,这个类主要用来帮助创建单例模式,其中主要的属性:


/** 缓存创建的单例对象: bean名字 --> bean对象 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** 缓存单例的factory,就是ObjectFactory这个东西,: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** 也是缓存创建的单例对象,功能和singletonObjects不一样,
在bean构造成功之后,属性初始化之前会把对象放入到这里,
主要是用于解决属性注入的循环引用: bean name --> bean instance
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

/** 记录在创建单例对象中循环依赖的问题,还记得Prototype中又记录创建过程中依赖的map吗?
在Prototype中只要出现了循环依赖就抛出异常,而在单例中会尝试解决 */
private final Set<String> singletonsCurrentlyInCreation =
 Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));

现在回过来看getSingleton(beanName, new ObjectFactory<Object>()这个方法的实现。


public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

synchronized (this.singletonObjects) {
 //尝试在singletonObjects中获取
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null) {
  //不存在则创建
  //把当前beanName加入到singletonsCurrentlyInCreation中
  beforeSingletonCreation(beanName);
  try {

singletonObject = singletonFactory.getObject();
  }
  ...
  finally {
   ...
   //从singletonsCurrentlyInCreation中删除beanName
   afterSingletonCreation(beanName);
  }
 }
 return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}

这段逻辑是不是和Prototype中解决循环类似,这里其实就是调用了ObjectFactory的getObject()获取对象,回过头去看前面代码,ObjectFactory的getObject()方法实际调用的是createBean(beanName, mbd, args)。说到createBean(beanName, mbd, args)又不得不说AbstractAutowireCapableBeanFactory这个类,主要功能就是完成依赖注入的Bean的创建,这个类的createBean方法代码如下,注意注解说明:


@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
...
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
...
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
  throws BeanCreationException {

// 实例化bean
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
 //如果没实例化则创建新的BeanWrapper
 //如果是通过构造器注入,这里是一个关键点
 /*
 因为在A初始化的时候发现构造函数依赖B,就会去实例化B,
 然后B也会运行到这段逻辑,构造函数中发现依赖A,
 这个时候就会抛出循环依赖的异常
 */
   instanceWrapper = createBeanInstance(beanName, mbd, args);
}

//如果当前是单例,并且allowCircularReferences为true(默认就是true,除非我们不希望Spring帮我们解决)
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
 /*
 !!!这里很重要,把构造成功,但属性还没注入的
 的bean加到singletonFactory中,这样再解决A的依赖
 过程中如果依赖A,就把这个半成品返回回去。
 */
 addSingletonFactory(beanName, new ObjectFactory<Object>() {
  @Override
  public Object getObject() throws BeansException {
   return getEarlyBeanReference(beanName, mbd, bean);
  }
 });
}

Object exposedObject = bean;
try {
 //自动注入属性
 populateBean(beanName, mbd, instanceWrapper);
 if (exposedObject != null) {
  exposedObject = initializeBean(beanName, exposedObject, mbd);
 }
}
...

return exposedObject;
}

注解已经注明了我的理解。就不再赘述

总结

上面代码是我一边debug一个写下的,现在写完了,根据自己的理解总结一下。

相关类说明

spring循环依赖策略解析

AbstractBeanFactory,这个类中包含了Bean创建的主要流程,在doGetBean这个方法中包含了对Prototype循环依赖处理。逻辑很简单,出现了循环依赖则直接抛出异常

DefaultSingletonBeanRegister 用于管理Singleton的对象的创建,以及解决循环依赖的问题,其中解决循环依赖的关键属性就是了earlySingletonObjects,他会在构造Singleton对象过程中暂时缓存构造成功,但属性还未注入的对象,这样就可以解决循环依赖的问题。

AbstractAutowireCapableBeanFactory,自动注入的相关逻辑,包自动注入的对象的创建、初始化和注入。但如果在调用构造函数中发现了循环依赖,则抛出异常

ObjectFactory,这个接口功能和FactoryBean类似,但是为了解决循环依赖,他决定了在获取的getSingleton()是一个完成品还是一个半成品。

思考

如果A--构造依赖->B,B--属性依赖-->A,例如:


@Component
public class BeanA {
private BeanB beanB;

@Autowired
public BeanA(BeanB beanB) {
 this.beanB = beanB;
}
}

@Component
public class BeanB {
@Autowired
private BeanA beanA;
}

这种情况会异常吗?提示:都有可能

来源:http://www.cnblogs.com/lizo/p/7401287.html

标签:spring,循环,依赖策略
0
投稿

猜你喜欢

  • Javaweb中Request获取表单数据的四种方法详解

    2023-10-07 03:07:02
  • java 中死锁问题的实例详解

    2022-08-18 02:22:39
  • C# TcpClient网络编程传输文件的示例

    2021-10-16 16:01:13
  • Java开发之内部类对象的创建及hook机制分析

    2023-11-27 04:45:20
  • Android快速实现发送邮件实例

    2022-06-10 03:39:48
  • java调用Restful接口的三种方法

    2021-09-07 16:49:04
  • Java多线程之synchronized同步代码块详解

    2022-02-10 21:29:51
  • 详解SpringBoot配置文件启动时动态配置参数方法

    2022-03-05 03:20:02
  • ElasticSearch查询文档基本操作实例

    2023-11-24 14:20:02
  • Java Main 函数启动不退出的解决方案

    2022-03-24 14:25:03
  • SpringCloud如何创建一个服务提供者provider

    2023-08-01 01:56:33
  • C#获取两个时间的时间差并去除周末(取工作日)的方法

    2022-07-25 00:20:44
  • 轻松实现Android仿淘宝地区选择功能

    2022-02-25 03:01:13
  • 六款值得推荐的android(安卓)开源框架简介

    2023-06-24 01:46:54
  • SQL语句删除和添加外键、主键的方法

    2023-04-16 22:18:35
  • C#面向对象设计原则之里氏替换原则

    2022-08-27 23:59:22
  • C++编程中用put输出单个字符和cin输入流的用法

    2023-05-28 06:14:49
  • 解决SpringMVC使用@RequestBody注解报400错误的问题

    2022-02-26 16:06:43
  • java理论基础Stream管道流状态与并行操作

    2021-08-31 14:24:07
  • javaWeb 四大域对象详细介绍

    2021-08-16 14:04:01
  • asp之家 软件编程 m.aspxhome.com