【1】Spring源码-Bean的加载(BeanFactory)

1. Spring Framework架构图

image.png

后面主要涉及到Beans、Context、AOP、Transactions、MVC等方面的内容。

2. Beans

2.1 BeanFactory的初始化

2.1.1 加载配置文件

  • 在Spring中,资源文件被统一封装成Resource对象, 资源处理可以用Resource提供的各种服务来进行操作。
  • 如:Resource classPathResource = new ClassPathResource("beanFactoryTest.xml");
  • 对不同来源的资源文件都有相应的Resource实现:文件(FileSystemResource)、Classpath资源(ClassPathResource)、URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等。
  • 有了Resource接口便可以对所有资源文件进行统一处理。至于实现,其实是非常简单的,以getInputStream为例,ClassPathResource中的实现方式便是通过class或者classLoader提供的底层方法进行调用,而对于FileSystemResource的实现其实更简单,直接使用FileInputStream对文件进行实例化。

2.1.2 创建BeanFactory

创建BeanFactory就更简单了,有时直接new一个出来。

protected DefaultListableBeanFactory createBeanFactory() {
   return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

2.1.3 加载BeanDefinitions

  • BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式,主要封装了每个Bean的相关信息。Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring容器配置信息的内存数据库,主要以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。
  • 以XmlBeanFactory为例,真正加载BeanDefinition的函数 doLoadBeanDefinitions()主要做了两件事情:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      Document doc = doLoadDocument(inputSource, resource);
      return registerBeanDefinitions(doc, resource);
   }
   catch (BeanDefinitionStoreException ex) {
      throw ex;
   }
   catch (SAXParseException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
   }
   catch (SAXException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "XML document from " + resource + " is invalid", ex);
   }
   catch (ParserConfigurationException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Parser configuration exception parsing XML from " + resource, ex);
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "IOException parsing XML document from " + resource, ex);
   }
   catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Unexpected exception parsing XML document from " + resource, ex);
   }
}
  1. 利用之前配置文件封装好的Resource对象加载XML文件,并得到对应的Document;
  2. 根据返回的Document注册Bean信息:doRegisterBeanDefinitions( );
        a. 获取Document的root节点,递归变量Document中的每一个节点;
        b. 解析每一个节点,注册BeanDefinition(在后续bean的加载中会用到);
            i. parseBeanDefinitions中会解析xml文件的各种标签,如<import>,<bean>,而<beans>该标签又会递归调用doRegisterBeanDefinitions。
// public static final String BEAN_ELEMENT = "bean";
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;


public static final String NESTED_BEANS_ELEMENT = "beans";

public static final String ALIAS_ELEMENT = "alias";

public static final String IMPORT_ELEMENT = "import";


private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}

        ii. 解析xml的各项标签完成后,还需要注册BeanDefinition(如processBeanDefinition()方法),包括通过beanName注册和通过别名alias注册(如果对某个bean配置了一个或多个别名,还需要将别名注册):
                 * 注册BeanDefinition就是将BeanDefinition放入map(BeanDefinitionMap,concurrentHashMap)中,key为beanName,value为BeanDefinition;
                 * this.beanDefinitionMap.put(beanName, beanDefinition);
                 * 通过alias注册(processAliasRegistration()方法): 将alias放入map(aliasMap,concurrentHashMap)中,key为alias,value为beanName;
        iii. 当每个bean被解析并注册完成后,会发出响应事件(fireComponentRegistered),通知相关的监听器。这里的实现只是为了扩展,当开发者需要对注册BeanDefinition事件进行监听时,可以通过注册监听器的方式并将处理逻辑写入监听器中,目前在spring中并没有对此事件做任何逻辑处理。
这些步骤其实也都是包含在ApplicationContext的初始化中哦,也就是ApplicationContext几乎包含BeanFactory的所有功能。

2.2 Bean的加载

加载Bean的开始,BeanFactory#getBean(String beanName)。

对于加载过程中所涉及的步骤大致如下:

  1. 转换对应beanName。
  • 或许很多人不理解转换对应beanName是什么意思,传入的参数name不就是beanName吗?其实不是,这里传入的参数可能是别名,也可能是FactoryBean,所以需要进行一系列的解析,这些解析内容包括如下内容。
    a. 去除FactoryBean的修饰符,也就是如果name="&aa",那么会首先去除&而使name="aa"。
    b. 指定alias所表示的最终beanName,例如别名A指向名称为B的bean则返回B;若别名A指向别名B,别名B又指向名称为C的bean则返回C。
  1. 尝试从缓存中加载单例。
  • 单例在Spring的同一个容器内只会被创建一次,后续再获取bean,就直接从单例缓存中获取了。当然这里也只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从singletonFactories中加载。因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean则直接使用ObjectFactory(后面还会说明)。
  1. bean的实例化。
  • 如果从缓存中得到了bean的原始状态,则需要对bean进行实例化。这里有必要强调一下,缓存中记录的只是最原始的bean状态,并不一定是我们最终想要的bean。举个例子,假如我们需要对工厂bean进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjectForBeanInstance就是完成这个工作的。
  1. 原型模式的依赖检查。
  • 只有在单例情况下才会尝试解决循环依赖,如果存在A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖,也就是情况:isPrototypeCurrentlyInCreation(beanName)判断true。
  1. 检测parentBeanFactory。
  • 从代码上看,如果缓存没有数据的话直接转到父类工厂上去加载了,这是为什么呢?
  • 可能读者忽略了一个很重要的判断条件:parentBeanFactory != null && !containsBeanDefinition (beanName),parentBeanFactory != null。parentBeanFactory如果为空,则其他一切都是浮云,这个没什么说的,但是!containsBeanDefinition(beanName)就比较重要了,它是在检测如果当前加载的XML配置文件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试下了,然后再去递归的调用getBean方法。
  1. 将存储XML配置文件的GernericBeanDefinition转换为RootBeanDefinition。
  • 因为从XML配置文件中读取到的Bean信息是存储在GernericBeanDefinition中的,但是所有的Bean后续处理都是针对于RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则会一并合并父类的属性。
  1. 寻找依赖。
  • 因为bean的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置成依赖于其他的bean,那么这个时候就有必要先加载依赖的bean,所以,在Spring的加载顺寻中,在初始化某一个bean的时候首先会初始化这个bean所对应的依赖。
  1. 针对不同的scope进行bean的创建。
  • 我们都知道,在Spring中存在着不同的scope,其中默认的是singleton,但是还有些其他的配置诸如prototype、request之类的。在这个步骤中,Spring会根据不同的配置进行不同的初始化策略。
  • createBean():

    * 利用反射创建Bean,BeanUtils.instantiateClass(constructorToUse),如果有动态代理则创建

    * 属性注入populateBean()方法注入属性,循环查找Bean所需注入的属性(分为autowiredByName和autowiredByType),使用getBean()方法获取并注入

    * 调用initializeBean方法完成用户自定义的初始化方法,该方法会先调用postProcessBeforeInitialization再调用InitializingBean.afterPropertiesSet(需要实现InitializingBean接口,实现用户自定义afterPropertiesSet方法)再调用postProcessAfterInitialization

  1. 类型转换。
  • 程序到这里返回bean后已经基本结束了,通常对该方法的调用参数requiredType是为空的,但是可能会存在这样的情况,返回的bean其实是个String,但是requiredType却传入Integer类型,那么这时候本步骤就会起作用了,它的功能是将返回的bean转换为requiredType所指定的类型。当然,String转换为Integer是最简单的一种转换,在Spring中提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。

以AbstractBeanFactory为例:

public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   /**
    * 1. 转换BeanName
    */
   final String beanName = transformedBeanName(name);
   Object bean;

   /**
    * 2. 尝试从缓存中加载单例
    * 首先尝试从缓存中加载,如果加载不成功则再次尝试从singletonFactories中加载。
    * 因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,
    * 在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,
    * 一旦下一个bean创建时候需要依赖上一个bean则直接使用ObjectFactory
    */
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      /**
       * 3. Bean的实例化
       * 判断得到的Bean是否为FactoryBean,如果是则从FactoryBean中获取bean实例(FactoryBean#getObject())
       *
       */
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }
   /**
    * 2.1 如果缓存中没有
    */
   else {
      /**
       * 4. 原型模式的依赖检查,如果原型模式有循环依赖则抛出异常
       */
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      /**
       * 5. 当父工厂不为空,且BeanDefinition在父工厂中,则从父工厂中获取Bean
       */
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // Not found -> check parent.
         String nameToLookup = originalBeanName(name);
         if (parentBeanFactory instanceof AbstractBeanFactory) {
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                  nameToLookup, requiredType, args, typeCheckOnly);
         }
         else if (args != null) {
            // Delegation to parent with explicit args.
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
      }

      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }

      try {
         /**
          * 6. 转换得到RootBeanDefinition
          * 在Spring中可以定义父子Bean,子类Bean可以继承父类Bean的属性,这里就是"递归向上"合并父子Bean的属性
          */
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         /**
          * 7. 寻找Bean的依赖
          */
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            for (String dep : dependsOn) {
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               registerDependentBean(dep, beanName);
               try {
                  /**
                   * 7.1 实例化所有依赖Bean
                   */
                  getBean(dep);
               }
               catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
               }
            }
         }

         /**
          * 8. 根据不同的scope创建Bean
          */
         if (mbd.isSingleton()) {
            /**
             * 8.1 创建Bean,并加入缓存
             */
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  /**
                   * 8.2 创建Bean
                   * 利用反射创建Bean,BeanUtils.instantiateClass(constructorToUse),如果有动态代理则创建
                   * 属性注入populateBean(),循环查找Bean所需注入的属性(分为autowiredByName和autowiredByType),使用getBean()方法获取并注入
                   * 调用initializeBean方法完成用户自定义的初始化方法,该方法会先调用postProcessBeforeInitialization再调用InitializingBean.afterPropertiesSet(需要实现InitializingBean接口,实现用户自定义afterPropertiesSet方法)再调用postProcessAfterInitialization
                   */
                  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;
               }
            });
            /**
             * 8.2 Bean的实例化
             */
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }

         else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }

         else {
            String scopeName = mbd.getScope();
            final 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);
                  }
               });
               bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            }
            catch (IllegalStateException ex) {
               throw new BeanCreationException(beanName,
                     "Scope '" + scopeName + "' is not active for the current thread; consider " +
                     "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                     ex);
            }
         }
      }
      catch (BeansException ex) {
         cleanupAfterBeanCreationFailure(beanName);
         throw ex;
      }
   }

   /**
    * 9. 类型转换
    */
   if (requiredType != null && !requiredType.isInstance(bean)) {
      try {
         T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
         if (convertedBean == null) {
            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
         }
         return convertedBean;
      }
      catch (TypeMismatchException ex) {
         if (logger.isDebugEnabled()) {
            logger.debug("Failed to convert bean '" + name + "' to required type '" +
                  ClassUtils.getQualifiedName(requiredType) + "'", ex);
         }
         throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
   }
   return (T) bean;
}

2.2.1 FactoryBean

  • 一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean 。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.Springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。

  • FactoryBean接口对于Spring框架来说占有重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。

  • 也就是说通过getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。

2.3 解决循环依赖

循环依赖的产生可能有很多种情况,例如:

  • A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象;
  • A的构造方法中依赖了B的实例对象,同时B的某个field或者setter需要A的实例对象;
  • A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象;

当然,Spring对于循环依赖的解决不是无条件的,首先前提条件是针对scope单例并且没有显式指明不需要解决循环依赖的对象,而且要求该对象没有被代理过。同时Spring解决循环依赖也不是万能,以上三种情况只能解决两种,第一种在构造方法中相互依赖的情况Spring也无法解决。
而第二种情况下,需要保证先初始化B,可以使用@DependsOn

2.3.1 单例模式(Singleton)的循环依赖

Spring循环依赖的理论依据其实是Java基于引用传递,当我们获取到对象的引用时,对象的field或者或属性是可以延后设置的。
Spring单例对象的初始化其实可以分为三步(就是上文中的createBean()方法):

  • createBeanInstance, 实例化,实际上就是调用对应的构造方法构造对象,此时只是调用了构造方法,spring xml中指定的property并没有进行populate
  • populateBean,填充属性,这步对spring xml中指定的property进行populate;
  • initializeBean,调用spring xml中指定的init方法,或者AfterPropertiesSet方法 ;

会发生循环依赖的步骤集中在第一步和第二步。

三级缓存
对于单例对象来说,在Spring的整个容器的生命周期内,有且只存在一个对象,很容易想到这个对象应该存在Cache中,Spring大量运用了Cache的手段,在循环依赖问题的解决过程中甚至使用了“三级缓存”。

“三级缓存”主要是指


/** Cache of singleton objects: bean name --> bean instance 已经初始化完成的实例*/

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** Cache of early singleton objects: bean name --> bean instance 只完成构造方法,还没有注入属性的bean*/

private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); 

从字面意思来说:singletonObjects指单例对象的cache,singletonFactories指单例对象工厂的cache,earlySingletonObjects指提前曝光的单例对象的cache。以上三个cache构成了三级缓存,Spring就用这三级缓存巧妙的解决了循环依赖问题。

  • Spring首先从singletonObjects(一级缓存)中尝试获取,如果获取不到并且对象在创建中,则尝试从earlySingletonObjects(二级缓存)中获取,如果还是获取不到并且允许从singletonFactories通过getObject获取,即通过singletonFactory#getObject()(三级缓存)获取,如果三级缓存都获取不到,则创建新的bean

  • singletonObjects,singletonFactories,earlySingletonObjects是互斥的,即一个bean只能存在于三者之一。如果bean被放入了earlySingletonObjects中则同时会remove singletonFactories中的bean。


protected 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);
         if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}
  • Spring解决循环依赖的诀窍就在于singletonFactories这个cache,这个cache中存的是类型为ObjectFactory(一个类似于FactoryBean的东西,可以通过getObject获取对象)

  • 此处就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来的。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光。

  • 这样做有什么好处呢?让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步(反射调用构造方法获得对象),并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory#getObject拿到A对象,B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B中的A对象完成了初始化;

  • 而“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”,A、B都在构造函数时需要拿到对方,而构造函数为执行完就无法使bean提前曝光,没放入singletonFacories中。

为什么用到三级缓存

  • 首先,使用两级缓存看起来也是可以解决循环依赖问题的,将只调用了构造方法创建出来的bean放在earlySingletonObjects,将初始化完成的bean放在singletonObjects中。似乎也能解决。

  • 但为什么还需要使用singletonFactories呢?主要原因是便于扩展,在doCreateBean()方法中通过反射(createBeanInstance)创建完bean后,如果允许提前曝光bean则将bean的ObjectFactory放入singletonFactories中。

//截取自AbstractAutowireCapableBeanFactory#doCreateBean()
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isDebugEnabled()) {
      logger.debug("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }
   addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

而ObjectFactory的getObject方法主要是通过以下方法实现(getEarlyBeanReference( )):

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;
}
  • 其实这个方法里没做什么,将通过反射(createBeanInstance)创建完的bean传入其中,用各种BeanPostProcessor处理bean,如果没有processor则原封不动的返回bean。也就是说使用singletonFactories的好处就是可以调用后置处理器,便于用户处理,AOP的advice也是在这里织入bean。

bean被加入缓存的时间

  • bean被加入三级缓存singletonFactories 是在创建bean(createBean方法)过程中,当利用构造器反射创建完成bean后,将bean加入singletonFactories中,然后才执行注入属性的操作;
  • 加入二级缓存earlySingletonObjects 是在BeanFactory#getBean()方法第一次尝试从缓存中获取bean时,getSingleton()方法:按顺序从一级、二级、三级缓存中获取,如果从三级缓存中获取到了,则加入二级缓存并返回,如果没有从三级缓存中获取到,则执行createBean创建新对象。

2.3.2 原型模式(Prototye)的循环依赖

在Spring中,@Controller默认都是单例的(singleton)。所谓单例,就是Spring的IOC机制只创建该类的一个实例,每次请求,都会用这同一个实例进行处理,因此若存在全局变量(成员变量),本次请求的值肯定会影响下一次请求时该变量的值。

使用成员变量后,若不想影响下次请求,就需要用到原型模式,即在Controller上加@Scope(“prototype”)

原型模式,指的是每次调用时,会重新创建该类的一个实例,比较类似于我们自己自己new的对象实例。

Spring对原型模式循环依赖的处理策略:

在原型模式下,如果存在A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖,所以在加载bean时,原型模式需要判断当前bean的所有依赖是否已被创建,如果创建了(即发生循环依赖)会抛出异常。只有在单例情况下才会尝试解决循环依赖。
在spring中解决循环依赖只对单例有效,而对于prototype的bean,spring没有好的解决办法,唯一要做的就是抛出异常

在单例模式中Spring通过缓存来解决循环依赖,而在原型模式中,由于每次调用都会创建一个对象,所以对象数量是不可控的,且每一个prototype都需要是全新的,因而无法进行缓存,所以Spring没有处理原型模式下的循环依赖。

仔细思考一下,其实prototype的循环引用其实不一定是有意义的。

  • singleton中持有prototype,prototype中持有singleton,spring是可以支持的。但是singleton中持有的prototype始终是同一个对象,这样似乎也就失去了prototype的意义。
  • prototype1中持有prototype2,prototype2中持有prototype1,spring不支持。当prototype1发现自己依赖prototype2,那么去创建prototype2,而prototype2发现自己依赖prototype1,又去创建prototype1,就会无限创建下去。因为每一个prototype都需要是全新的。所以spring不允许prototype的循环依赖。

参考:https://blog.csdn.net/f641385712/article/details/92801300

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容