乐于分享
好东西不私藏

源码分析:Spring如何解决单例Bean的循环依赖?

源码分析:Spring如何解决单例Bean的循环依赖?

Spring如何解决单例Bean的循环依赖?

首先看下DefaultSingletonBeanRegistry的getSingleton(String, boolean)这个方法在Spring中如何获取单例Bean的?

举例说明:

假设Bean为A,第一次对A进行依赖查找时是查不到beanName对应实例的,因为● singletonObjects:不存在A对应元素,A完全初始化之后才会放进去。● singletonsCurrentlyInCreation:也不存在A对应元素!用以标识创建中的状态!

看下这个getSingleton(String beanName, boolean allowEarlyReference)方法:

//DefaultSingletonBeanRegistry.java     /** Cache of singleton objects: bean name to bean instance. */    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);    /** Cache of singleton factories: bean name to ObjectFactory. */    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);    /** Cache of early singleton objects: bean name to bean instance. */    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);    /** Set of registered singletons, containing the bean names in registration order. */    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);    /** Names of beans that are currently in creation. */    private final Set<String> singletonsCurrentlyInCreation =            Collections.newSetFromMap(new ConcurrentHashMap<>(16));/** * Return the (raw) singleton object registered under the given name. * <p>Checks already instantiated singletons and also allows for an early * reference to a currently created singleton (resolving a circular reference). * @param beanName the name of the bean to look for * @param allowEarlyReference whether early references should be created or not * @return the registered singleton object, or {@code null} if none found */@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {   Object singletonObject = this.singletonObjects.get(beanName);   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {        synchronized (this.singletonObjects) {             singletonObject = this.earlySingletonObjects.get(beanName);             //下次进入时singletonObject已经提前暴漏则跳过下边逻辑直接返回。             if (singletonObject == null && allowEarlyReference) {                //返回提前暴漏的Bean。之所以能解决循环依赖,就是其他Bean依赖当前BeanA的时候,                //singletonFactories返回了A的beanName关联的ObjectFactory,                //而这个ObjectFactory#getObject()可以提前暴漏未完全初始化的Bean A实例,其实是一个                //BeanWrapper对象,尚未填充属性和初始化Bean。                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);                if (singletonFactory != null) {                   singletonObject = singletonFactory.getObject();                   this.earlySingletonObjects.put(beanName, singletonObject);                   this.singletonFactories.remove(beanName);                }             }          }   }    return singletonObject;}

所以第一次执行上述代码中的getSingleton时,其实返回的shareInstance为null,也就是下边doGetBean中的shareInstance。(这里忽略一些无关代码)假使代码执行到

getSingleton(String beanName, 

ObjectFactory<?> objectFactory)

//AbstractBeanFactory.javaprotected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {     ....      Object sharedInstance = getSingleton(beanName, true);      .....      // Create bean instance.        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;              }           });           bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);        }        .....        return bean;}

通过代码细节看到singleObjects中找不到beanName对应的实例,于是后边会根据singletonFactory.getObject()来查找Bean。

singletonFactory就是上述代码中传进来的包含createBean的lambda表达式。

但在createBean表达式执行之前,需要做一些前置准备。也就是执行这个:

beforeSingletonCreation(String beanName);

此方法中执行的操作就是“标志A这个Bean在创建中”,

singletonsCurrentlyInCreation.add(String BeanName)

protected void beforeSingletonCreation(String beanName) {    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {        throw new BeanCurrentlyInCreationException(beanName);   }}
//DefaultSingletonBeanRegistry.javapublic Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {    ....    synchronized (this.singletonObjects) {        Object singletonObject = this.singletonObjects.get(beanName);        if (singletonObject == null) {            ...            //this.singletonsCurrentlyInCreation.add(beanName) 标志A这个Bean在创建中            beforeSingletonCreation(beanName);            ...            try {                //AbstractAutowireCapableBeanFactory#createBean               singletonObject = singletonFactory.getObject();               newSingleton = true;            }catch(...){}            finally{                ...                //this.singletonsCurrentlyInCreation.remove(beanName)                afterSingletonCreation(beanName);            }            if (newSingleton) {               // addSingleton代码贴在这里,方便查看               // 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);               //       }                // }                            addSingleton(beanName, singletonObject);            }             }        return singletonObject;    }}

创建完Bean以后,即createBean方法执行完毕之后,在finally块中就删除掉Bean A创建中的标识了,即:

afterSingletonCreation(beanName);

实际就是下面这一行代码起了作用:

this.singletonsCurrentlyInCreation.remove(beanName)** 

Bean A创建完了,但还需要执行下边操作:

 if (newSingleton) {                   addSingleton(beanName, singletonObject); }     

这个操作的作用是更新Bean在Spring容器中的缓存状态。

   protected void addSingleton(String beanName, Object singletonObject) {           synchronized (this.singletonObjects) {                   //缓存Bean                this.singletonObjects.put(beanName, singletonObject);                  //删除beanName关联的用以提前暴漏Bean的ObjectFactory                 this.singletonFactories.remove(beanName);                 //删除早期暴漏bean缓存标识                 this.earlySingletonObjects.remove(beanName);                 //注册为单例Bean                 this.registeredSingletons.add(beanName);             }  }  

在createBean中执行Bean A实例化的主要就是doCreateBean。这个方法里判断:

  • • 单例Bean
  • • 允许循环依赖
  • • 正在创建中

能满足这个三个条件,则Spring会认为这个此时A是需要提前暴漏的单例Bean。因为只有提前暴漏才能解决循环依赖的问题。那么,如何提前暴漏单例Bean A呢?

如何提前暴漏Bean?

Spring的实现其实很简单,就是给A关联一个ObjectFactory对象。然后再进行Bean的初始化进程即populateBean和initializeBean,最后返回初始化之后的Bean。然后下面这一行就可以返回了。

getSingleton(String beanName, ObjectFactory<?> singletonFactory)中singletonFactory.getObject();

拿到了createBean之后的Bean A实例,相当于Bean A已经创建完成了,那么执行finally里的afterSingletonCreation方法。从singletonsCurrentlyInCreation中删除beanName(毕竟Bean已经创建结束了),  就是前边已经提到的finally块中的afterSingletonCreation(beanName);这行代码。

但是本文核心问题来了,如果Bean A依赖了Bean C,那么createBean方法执行时必然会去依赖查找Bean C,而Bean C同样执行getBean方法来实例化和初始化,在这个过程中Bean C如果依赖了Bean A,就形成了A–C—A这样的循环依赖。Bean A虽然还没初始化完成,但是在getBean查找Bean C之前却提前暴漏了自己。也就是这行代码:

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

此时怎么办呢?

//AbstractAutowireCapableBeanFactoryprotected Object doCreateBean(    final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)                                                                throws BeanCreationException {      .........    // Eagerly cache singletons to be able to resolve circular references    // even when triggered by lifecycle interfaces like BeanFactoryAware.    //单例Bean&允许循环依赖&创建中,则提前暴漏bean    boolean earlySingletonExposure =                         (mbd.isSingleton() && this.allowCircularReferences &&                                                  isSingletonCurrentlyInCreation(beanName));    if (earlySingletonExposure) {        if (logger.isTraceEnabled()) {            logger.trace("Eagerly caching bean '" + beanName                             +"' to allow for resolving potential circular references");        }        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));     }    // Initialize the bean instance.    Object exposedObject = bean;    try {       populateBean(beanName, mbd, instanceWrapper);       exposedObject = initializeBean(beanName, exposedObject, mbd);    }catch(...){...}    if (earlySingletonExposure) {        Object earlySingletonReference = getSingleton(beanName, false);        if (earlySingletonReference != null) {            if (exposedObject == bean) {                  exposedObject = earlySingletonReference;            }            .........        }    }    return exposedObject;}

重点看下提前暴漏都做了啥?

 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
/** * Add the given singleton factory for building the specified singleton * if necessary. * <p>To be called for eager registration of singletons, e.g. to be able to * resolve circular references. * @param beanName the name of the bean * @param singletonFactory the factory for the singleton object */protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {    Assert.notNull(singletonFactory, "Singleton factory must not be null");    synchronized (this.singletonObjects) {     if (!this.singletonObjects.containsKey(beanName)) {         //关键代码,beanName关联singletonFactory         this.singletonFactories.put(beanName, singletonFactory);         this.earlySingletonObjects.remove(beanName);         this.registeredSingletons.add(beanName);      }   }}

给Bean A关联了一个ObjectFactory,也就是getEarlyBeanReference对应的lambda函数对象。放入了singletonFactories中,当Bean C去查BeanA的时候就可以通过getEarlyBeanReference方法获取exposedObject这个早期bean对象。

/** * Obtain a reference for early access to the specified bean, * typically for the purpose of resolving a circular reference. * @param beanName the name of the bean (for error handling purposes) * @param mbd the merged bean definition for the bean * @param bean the raw bean instance * @return the object to expose as bean reference */protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {   Object exposedObject = bean;   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {        for (BeanPostProcessor bp : getBeanPostProcessors()) {        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);         }      }   }    return exposedObject;}

看下Bean C怎么依赖查Bean A?

/** * Return the (raw) singleton object registered under the given name. * <p>Checks already instantiated singletons and also allows for an early * reference to a currently created singleton (resolving a circular reference). * @param beanName the name of the bean to look for * @param allowEarlyReference whether early references should be created or not * @return the registered singleton object, or {@code null} if none found */@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {   Object singletonObject = this.singletonObjects.get(beanName);   //如果C来查找A,此时会符合if条件   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {        synchronized (this.singletonObjects) {            //这里C来查A一定查不到,符合下边的if条件             singletonObject = this.earlySingletonObjects.get(beanName);             if (singletonObject == null && allowEarlyReference) {                 //C来查A,singletonFactories是有值的                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);                if (singletonFactory != null) {                   //调用getEarlyBeanReference返回exposedObject                   singletonObject = singletonFactory.getObject();                   //全局也只有此处对earlySingletonObjects进行put                   this.earlySingletonObjects.put(beanName, singletonObject);                   this.singletonFactories.remove(beanName);                }             }          }   }    return singletonObject;}

到这里,基本上已经分析完了关于单例Bean循环依赖的问题,应该还是挺清晰的

总结一下

Spring通过ObjectFactory提前暴漏一个创建中的Bean(BeanWrapper).给依赖它的Bean使用。让依赖它的Bean的初始化不再需要等待其完全初始化,避免了循环依赖的问题!

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 源码分析:Spring如何解决单例Bean的循环依赖?

评论 抢沙发

1 + 3 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮