preface

Personally, I think the biggest charm of Spring is dependency injection. From this article, we will sort out and review the IOC part of Spring again. Let’s start with the getBean(String) of the BeanFactory. The length of this article may be slightly longer, please forgive fat friends


1. Strategic overlookgetBean

1.1 getBeanDeclaration and implementation of

getBean

  • Method declaration in org.springframework.beans.factory.BeanFactory.
  • Implementation inorg.springframework.beans.factory.support.AbstractBeanFactory

There’s a lot to be said about BeanFactory and its implementation classes, but we’ll get into that later

1.2 source town building

1.2.1 getBean

/** * empty shell method, DoGetBean * @param name the name of the bean to retrieve * @return * @throws BeansException */ @override public  Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }Copy the code
  • GetBean is essentially an empty shell method, the implementation of which is all theredoGetBeanIn this method (familiarSpringFat people are probably used to this.)

1.2.2 doGetBean


/**
 * 获取bean
 *
 * @param name          bean的name
 * @param requiredType  获取的bean的类型,默认是null
 * @param args          实例化参数,默认是null
 * @param typeCheckOnly
 * @param <T>
 * @return
 * @throws BeansException 抛出异常
 */
protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {
   /**
    * <1> 获取beanName
    * 这一步操作主要是有2个原因
    * 1. 别名,Spring是支持bean别名的,我们要向获取真正的bean,必须要拿到最原始的beanName
    * 2. 工厂方法  当name以 & 字符开头时说明调用者想获取 FactoryBean本身,而不是FactoryBean所实现的类,
    *    但是BeanFactory中 普通bean和 FactoryBean本身的存储方式都是一致的,所以需要将 & 符号移除
    */
   String beanName = transformedBeanName(name);
   Object beanInstance;

   /**
    * 从缓存中获取单例 bean。
    * Spring 是使用 Map 作为 beanName 和 bean 单例bean实例的缓存的(其中key-> beanName, value -> bean)Map本身就不允许key重复
    * 所以这里暂时可以把 getSingleton(beanName) 等价于 beanMap.get(beanName)。当然实际原理并没有这么简单哈
    */
   Object sharedInstance = getSingleton(beanName);
   /**
    * 如果 sharedInstance == null,说明还没初始化好(BeanFactory并不会一开始就初始化好所有的bean,而是等到需要的时候才初始化--懒加载)
    * 如果 sharedInstance !=null 那 args 也没啥用了,因为不会再初始化一次
    */
   if (sharedInstance != null && args == null) {
      //打印的一些日志,不需要理会,其中  isSingletonCurrentlyInCreation 是判断单例bean是不是在创建中
      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 + "'");
         }
      }
      /**
       * 1 sharedInstance 如果是个普通的单例bean,该方法会直接返回bean,
       * 2 sharedInstance 如果是个FactoryBean类型的,则需要调用 getObject 获取真正的FactoryBean然后返回《在这里,普通bean 和FactoryBean被一视同仁,只不过获取方式不大一样而已》
       */
      beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   } else {

      /**
       * 走到这里则说明 sharedInstance 可能是空,有2中可能性
       * 1 beanName对应的实例还没创建好,
       * 2 beanName对应的实例可能在父容器中创建了,需要再父容器查找
       */

      /**
       * BeanFactory 不会缓存Prototype类型的bean,直接抛出异常
       */
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      /**
       * 如果sharedInstance == null,先去父容器去查找
       */
      //获取父容器
      BeanFactory parentBeanFactory = getParentBeanFactory();
      /**
       * 如果父容器不为null, 且 beanDefinitionMap 不包含该bean
       */
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         /**
          * 获取 name 对应的 beanName,如果 name 是以 & 字符开头,则返回 & + beanName
          * 这里和上面有点区别,先是处理别名 然后如果该beanName是 factoryBean name的话,会把 & 还原
          */
         String nameToLookup = originalBeanName(name);
         /**
          * 如果父容器是  AbstractBeanFactory 或者其子类,
          */
         if (parentBeanFactory instanceof AbstractBeanFactory) {
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                  nameToLookup, requiredType, args, typeCheckOnly);
         } else if (args != null) {
            /**
             * 如果初始化参数不为null
             */
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         } else if (requiredType != null) {
            /**
             * 如果 requiredType 不为null
             */
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         } else {
            return (T) parentBeanFactory.getBean(nameToLookup);
         }
      }

      /**
       * 如果 typeCheckOnly 参数为false, 标记该bean正在创建中
       */
      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }

      StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
            .tag("beanName", name);
      try {
         if (requiredType != null) {
            beanCreation.tag("beanType", requiredType::toString);
         }
         /**
          * 合并父 BeanDefinition 与子 BeanDefinition
          */
         RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         /**
          * 检查是否有依赖,如果有的话,确保其依赖优先初始化
          * 该方法获取当前bean的依赖项
          */
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            for (String dep : dependsOn) {
               /**
                * 检测是否存在 depends-on 循环依赖,若存在则抛异常。比如 A 依赖 B,B 又依赖 A,他们的配置如下:
                *   <bean id="beanA" class="BeanA" depends-on="beanB">
                *   <bean id="beanB" class="BeanB" depends-on="beanA">
                * beanA 要求 beanB 在其之前被创建,但 beanB 又要求 beanA 先于它
                * 创建。这个时候形成了循环,对于 depends-on 循环,Spring 会直接
                * 抛出异常
                *
                * 这里会有2个Map分别是 dependentBeanMap  dependenciesForBeanMap
                * dependentBeanMap key-> beanName, value -> 依赖该bean的所有bean集合   记录谁依赖当前bean
                * dependenciesForBeanMap  key-> beanName, value->该bean依赖的所有bean 集合 记录当前bean依赖谁
                * 思路清奇啊
                */
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               /**
                * 这一步主要是注册依赖记录,不然上一步的数据木得呀
                * 将当前beanName注册到   dependentBeanMap, 将dep注册到 dependenciesForBeanMap
                */
               registerDependentBean(dep, beanName);
               try {
                  /**
                   * 依次加载所依赖的bean,重复操作
                   */
                  getBean(dep);
               } catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
               }
            }
         }

         /**
          * 如果是单例bean
          */

         if (mbd.isSingleton()) {
            /**
             * 这里并没有直接调用 createBean 方法创建 bean 实例,而是通过 
             * getSingleton(String, ObjectFactory) 方法获取 bean 实例。
             * getSingleton(String, ObjectFactory) 方法会在内部调用 
             * ObjectFactory 的 getObject() 方法创建 bean,并会在创建完成后,
             * 将 bean 放入缓存中。关于 getSingleton 方法的分析,本文先不展开 后面的文章中进行分析
             */
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  return createBean(beanName, mbd, args);
               } catch (BeansException ex) {
                  /**
                   * 发生异常的话 销毁bean
                   */
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            /**
             * 如果bean是FactoryBean, 则调用工厂方法获取真正的bean,单例则直接返回
             */
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         } else if (mbd.isPrototype()) {
            /**
             * 创建原型模式bean
             */
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            } finally {
               afterPrototypeCreation(beanName);
            }
            beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         } else {
            /**
             * 其他类型的bean
             */
            String scopeName = mbd.getScope();
            if (!StringUtils.hasLength(scopeName)) {
               throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
            }
            Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
               throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
            }
            try {
               Object scopedInstance = scope.get(beanName, () -> {
                  beforePrototypeCreation(beanName);
                  try {
                     return createBean(beanName, mbd, args);
                  } finally {
                     afterPrototypeCreation(beanName);
                  }
               });
               beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            } catch (IllegalStateException ex) {
               throw new ScopeNotActiveException(beanName, scopeName, ex);
            }
         }
      } catch (BeansException ex) {
         beanCreation.tag("exception", ex.getClass().toString());
         beanCreation.tag("message", String.valueOf(ex.getMessage()));
         cleanupAfterBeanCreationFailure(beanName);
         throw ex;
      } finally {
         beanCreation.end();
      }
   }

   /**
    * 会做一些类型转换的事情
    */
   return adaptBeanInstance(name, beanInstance, requiredType);
}
Copy the code

Personally, I think these two Beanmaps are easy to be confused, so they are posted here for your reference only

  • DependentBeanMap Key -> beanName, value -> Set of all beans that depend on this bean
  • DependenciesForBeanMap Key -> beanName, value-> Set of beans on which the current bean depends

The flow chart is as follows

2 Detailed code analysis

2.1 beanName conversion

/** * Return beanName * @param * @return */ protected String transformedBeanName(String name) {return canonicalName(BeanFactoryUtils.transformedBeanName(name)); } /** * If name begins with an &, this operation is used to give a FactoryBean special treatment. After all, in BeanFactory, FactoryBean is no different from ordinary Bean * for example, name = "&&&&&person", Person * @param name * @return */ public static String transformedBeanName(String name) {assert.notnull (name, "'name' must not be null"); if (! name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { return name; } return transformedBeanNameCache.computeIfAbsent(name, beanName -> { do { beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); } while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); return beanName; }); } /** * is mainly a way out alias, there is a loop * this uses a while loop, the reason is: there can be multiple aliases, that is, aliases point to aliases. For example:  * <bean id="hello1" class="service.Hello"/> * <alias name="hello1" alias="aliasA1"/> * <alias name="aliasA1" AliasB -> aliasA1 -> hello1 alias="aliasB"/> * <p> AliasMap = [<aliasB, aliasA1>, <aliasA1, hello1>]. Public String canonicalName(String name) {String * * @param * @return */ public String canonicalName(String name) {String canonicalName = name; String resolvedName; do { resolvedName = this.aliasMap.get(canonicalName); if (resolvedName ! = null) { canonicalName = resolvedName; } } while (resolvedName ! = null); return canonicalName; }Copy the code

2.2 Obtaining beans from the cachegetSingleton

We all know that Spring only instantiates a singleton Bean once, so how can we ensure that the same Bean is returned multiple times? The answer is cache, of course, this is not a Redis cache! Without further ado, go to the code

@Override @Nullable public Object getSingleton(String beanName) { return getSingleton(beanName, true); } /** ** @param beanName * @param allowEarlyReference Whether to allow other beans to reference the bean being created, * @return */ @nullable protected Object getSingleton(String beanName, Boolean allowEarlyReference) {** * singletonObjects is ConcurrentHashMap, key->beanName, value->bean */ Object singletonObject = this.singletonObjects.get(beanName); /** * If singletonObject==null Or are created in the * * / if (singletonObject = = null && isSingletonCurrentlyInCreation (beanName)) {/ * * * from earlySingletonObjects From early exposure of bean, used for processing circular reference * / singletonObject = this. EarlySingletonObjects. Get (beanName); /** * If singletonObject==null Synchronized (this.singletonObjects) {if (singletonObject == null &&alloWearlyReference) {synchronized (this.singletonObjects) { singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); If (singletonObject == null) {/** * get the corresponding factory class from the bean factory cache */ ObjectFactory<? > singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory ! = null) {/** * If the factory class is not null and assigned to singletonObject * put singletonObject in the earlySingletonObjects cache, Can solve the follow-up of circular dependencies * remove beanFacotry at the same time, the subsequent will not be used to * / singletonObject = singletonFactory. GetObject (); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } } } return singletonObject; }Copy the code

There are three cache collections in the code above, which are the focus of the interview, and we’ll talk about them briefly

Cache name use
singletonObjects Used to store beans that have been fully initialized and can be used directly
earlySingletonObjects Used to hold beans that are still being initialized to resolve loop dependencies
singletonFactories The user stores the bean factory. The beans generated by the bean factory are not fully initialized. The beans generated by the bean factory will be placed in the next stepearlySingletonObjects

2.3 Merging Father and SonBeanDefinition

Believe there are some fat friends like me, a little confused on the bean’s merger getMergedLocalBeanDefinition, actually we say father and son BeanDefinition configuration as follows, stu inherited person, can cover the configuration in the parent class

<bean id="person" class="com.fenghuolun.spring.nazha.bean.Person">
    <property name="content" value="tec"/>
</bean>

<bean id="stu" parent="person">
    <property name="content" value="child"/>

</bean>
Copy the code
Protected RootBeanDefinition getMergedLocalBeanDefinition (String beanName) throws BeansException {/ / detection Does mergedBeanDefinitions already contain the beanName bean? If so, Direct return RootBeanDefinition MBD = this. MergedBeanDefinitions. Get (beanName); if (mbd ! = null && ! mbd.stale) { return mbd; } getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); } protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException { return getMergedBeanDefinition(beanName, bd, null); } /** * * @param beanName * @param bd * @param containingBd * @return * @throws BeanDefinitionStoreException */ protected RootBeanDefinition getMergedBeanDefinition( String beanName, BeanDefinition bd, @ Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException {/ / synchronization lock synchronized (this.mergedBeanDefinitions) { RootBeanDefinition mbd = null; RootBeanDefinition previous = null; // containingBd = null From the cache to obtain the corresponding baen assigned to MBD if (containingBd = = null) {MBD = this. MergedBeanDefinitions. Get (beanName); } if (mbd == null || mbd.stale) { previous = mbd; // If bd.getParentName()==null, there is no parent configuration Update BeanDefinition if (bd.getParentName() == null) {// Update BeanDefinition if (bd instanceof) RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { mbd = new RootBeanDefinition(bd); } } else { BeanDefinition pbd; ParentBeanName = transformedBeanName(bd.getParentName()); /** * beanName is the same as its subclass beanName. If the beanName is the same as its subclass beanName, the bean must be in the parent container. Beanname.equals (parentBeanName)) {** * getMergedBeanDefinition: beanname.equals (parentBeanName)) {** * getMergedBeanDefinition: beanname.equals (parentBeanName); Use to merge a parent BeanDefinition with a grandparent BeanDefinition. PBD = getMergedBeanDefinition(parentBeanName); PBD = getMergedBeanDefinition(parentBeanName); / / BeanFactory parent = getParentBeanFactory(); / / BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without a ConfigurableBeanFactory parent"); } } } catch (NoSuchBeanDefinitionException ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex); } /** * Create a RootBeanDefinition based on the configuration of the parent BeanDefinition. MBD = new RootBeanDefinition(PBD); MBD = new RootBeanDefinition(PBD); mbd.overrideFrom(bd); } /** * Default singleton mode */ if (! StringUtils.hasLength(mbd.getScope())) { mbd.setScope(SCOPE_SINGLETON); } /** * rewrites the scope attribute of MDB */ if (containingBd! = null && ! containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } if (previous ! = null) { copyRelevantMergedBeanDefinitionCaches(previous, mbd); } return mbd; }}Copy the code

2.4 fromFactoryBeanTo get the bean instance

protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @ Nullable RootBeanDefinition MBD) {/ / if the name begin with & the if (BeanFactoryUtils. IsFactoryDereference (name)) {if (beanInstance instanceof NullBean) { return beanInstance; } // If beanInstance is not a FactoryBean, throw an exception if (! (beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } // Set MBD to factory Bean if (MBD! = null) { mbd.isFactoryBean = true; } return beanInstance; } /** * At this point, beanInstance is either a factory bean or a normal bean, in which case it returns */ if (! (beanInstance instanceof FactoryBean)) { return beanInstance; } // beanInstance must bea FactoryBean Object Object = null; if (mbd ! = null) { mbd.isFactoryBean = true; } else {/** * if MBD is null, Get beanName's corresponding FactoryBean from the cache * The singleton beans generated by a FactoryBean are placed in the factoryBeanObjectCache Map */ object = getCachedObjectForFactoryBean(beanName); } if (object == null) {// If object == null, convert beanInstance to FactoryBean FactoryBean<? > factory = (FactoryBean<? >) beanInstance; // Merge BeanDefinition if (MBD == null && containsBeanDefinition(beanName)) {MBD = getMergedLocalBeanDefinition(beanName); } /** * synthetic = (MBD! = null && mbd.isSynthetic()); Object = getObjectFromFactoryBean(factory, beanName,! synthetic); } return object; } protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName, Boolean shouldPostProcess) {/** * if factory is a singleton and singletonObjects contain beanName, * Note that factoryBeans also have singleton and non-singleton modes, */ if (factory.issingleton () &&containsSingleton (beanName)) {synchronized (getSingletonMutex()) {synchronized (getSingletonMutex()) { / / the cache access bean Object Object = this. FactoryBeanObjectCache. Get (beanName); If (object = = null) {/ / bean = = null, use the factory get bean object = doGetObjectFromFactoryBean (factory, beanName); Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere ! = null) { object = alreadyThere; } else {// If yes! Synthetic if (shouldPostProcess) {/ / the current bean is created, direct return the if (isSingletonCurrentlyInCreation (beanName)) {return object; } / / added to singletonsCurrentlyInCreation beforeSingletonCreation (beanName); Try {/ / rear processing, there is no do what actually object = postProcessObjectFromFactoryBean (object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } the finally {/ / removed from singletonsCurrentlyInCreation afterSingletonCreation (beanName); } // The instance created by a FactoryBean is cached in the factoryBeanObjectCache. For the use of subsequent calls the if (containsSingleton (beanName)) {this. FactoryBeanObjectCache. Put (beanName, object); } } } return object; } } else { Object object = doGetObjectFromFactoryBean(factory, beanName); if (shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } return object; }}Copy the code

Focus on being creative and afterSingletonCreation

protected void beforeSingletonCreation(String beanName) { if (! this.inCreationCheckExclusions.contains(beanName) && ! this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } protected void afterSingletonCreation(String beanName) { if (! this.inCreationCheckExclusions.contains(beanName) && ! this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); }}Copy the code

Careful fat friends will find singletonsCurrentlyInCreation this cache is used Specific role is what, the door has a specific instructions. I won’t go into details here.