【spring源码】spring中bean的创建流程,三级缓存以及循环依赖问题

·  阅读 870
【spring源码】spring中bean的创建流程,三级缓存以及循环依赖问题

这是我参与更文挑战的第1天,活动详情查看: 更文挑战

前面的文章主要写了spring源码工厂后置处理器所干的事情,即spring工程的扫描,解析,得到beanDefinition以及所有的beanName,还有注解@Import的原理,以及引入ImportSelectorImportBeanDefinitionRegistrar的应用,有兴趣的可以看看前面的文章。

这篇文章开始写bean的实例化过程,读源码比较枯燥,相关的文章也比较枯燥,我尽量写简单明了点。

1.概述

bean创建过程.png

上面的图大概画了整个bean的创建流程,spring在创建bean之前首先从缓存池中去getBean,当从缓存池中获取不到bean时,才会去创建bean。了解spring源码的同学知道,spring中有三级缓存:

  • 一级缓存:singletonObjects ---> 存放完整bean的单例池
  • 二级缓存:earlySingletonObjects ---> 存放非完整bean的单例池,存放早期暴露的bean
  • 三级缓存:singletonFactories ---> 存放可以创建bean的factory,这里的factory主要是去创建bean的代理对象

在创建bean的过程中,会通过bean后置处理去拿到bean的构造方法,对bean进行创建,然后对bean进行包装代理,如果bean不需要进行包装代理,就会返回一个早期暴露的对象,并放入二级缓存中,如果需要进行包装代理,就会返回一个factory,并放入三级缓存中。

然后会组装bean,这个过程就是去装配bean的属性,当bean装配完成后,bean就算是一个完整的bean了,最后会把这个完整的bean放入单例池中,即一级缓存中。

这就是bean创建的整个过程,这里概述了一下,也是想读到这篇文章的同学心里有一个大概,后面不至于晕乎乎的,下面就进入源码阅读阶段吧。

2.从缓存池中getBean

我们直接看doGetBean()这个方法,方法路径:AbstractBeanFactory#doGetBean(),在该方法最开始的时候就是去从单例池获取bean:

缓存getBean.png

接下来就点进这个方法看看是怎么从缓存中获取bean的:

getBean.png

可以看到代码中,首先是从一级缓存中去拿bean,当拿不到bean时,且当前bean正在被创建,就会从二级缓存中去获取,当二级拿不到时,从三级缓存中获取factory对象,如果拿到了factory对象,会从factory中去获取bean,最后将该bean放入二级缓存中。

由于spring工程在启动时,缓存里肯定没有bean对象,于是就需要去创建bean,接下来看bean是如何创建的。

3.bean的创建

还是doGetBean()方法,看一下最重要的代码片段:

getSingleton2.png

这里有两个方法,先看方法1,即createBean(beanName, mbd, args);点进这个方法去看看,这里省略了一些不重要的代码:

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {

   //... ...

   // Prepare method overrides.
   try {
      /**
       * 处理lookup-Method 和replace-Method,统称为overrides
       */
      mbdToUse.prepareMethodOverrides();
   }
   //... ...

   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      /**
       * 里面有个后置处理器,如果这个后置处理器能返回一个bean,就直接返回了,不再进行后面的装配,不理会里面的依赖
       * InstantiationAwareBeanPostProcessor
       */
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
  //... ...

   try {
      /**
       * 创建对象
       */
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isTraceEnabled()) {
         logger.trace("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   }
  //... ...
}
复制代码

可以看到这段代码片段里,首先是去处理overrides,然后去调用一个bean后置处理(InstantiationAwareBeanPostProcessor)的方法,如果该方法返回了一个对象,则返回该对象,不进行后面的bean创建流程。

接下来看看创建对象这个方法:Object beanInstance = doCreateBean(beanName, mbdToUse, args);

在这个方法里,我们一部分一部分的来看,第一部分就是去创建bean对象:

createBean1.png

来看看创建包装对象的部分,点进这个方法中:instanceWrapper = createBeanInstance(beanName, mbd, args); 依然省略一部分不重要的代码:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   //... ...
   /**
    * supplier的方式实例化对象
    */
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   /**
    * 如果 FactoryMethodName 不为空,通过 instantiateUsingFactoryMethod() 去实例化对象
    * 通过xml去指定  FactoryMethodName
    * @Bean  当是static的时候,相当于是给对象配置了一个 FactoryMethodName
    *   不是static的时候, 是uniqueFactoryMethodName
    *   具体看ConfigurationClassPostProcessor
    */
   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }

   // Shortcut when re-creating the same bean...
   /**
    * 从Spring的原始注释中,可以知道这是一个Shortcut(快捷方式),什么意思呢?
    * 当多次构建同一bean时,可以使用这个 Shortcut
    * 也就是不需要再次推断使用哪种方式构造bean
    */
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            autowireNecessary = mbd.constructorArgumentsResolved;
         }
      }
   }
   if (resolved) {
      if (autowireNecessary) {
         return autowireConstructor(beanName, mbd, null, null);
      }
      else {
         return instantiateBean(beanName, mbd);
      }
   }

   // Candidate constructors for autowiring?
   /**
    * 由后置处理器(SmartInstantiationAwareBeanPostProcessor)决定 返回 有参数的 构造方法
    * 如果只有一个无参构造方法,这里返回为 null
    */
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   /**
    * 自动装配模型 !=  自动装配技术
    * 自动装配模型  默认为 0
    */
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }

   // Preferred constructors for default construction?
   ctors = mbd.getPreferredConstructors();
   if (ctors != null) {
      return autowireConstructor(beanName, mbd, ctors, null);
   }

   // No special handling: simply use no-arg constructor.
   /**
    * 通过默认的无参构造方法进行初始化
    */
   return instantiateBean(beanName, mbd);
}
复制代码

该方法主要是讲如何去创建bean,首先是通过supplier的方式实例化对象,其次是通过FactoryMethodName的方式去实例化对象(这种方式好像没用过,有大神知道吗),再然后是通过bean后置处理器去找到bean的构造方法,如果bean中定义了有参构造方法,就返回有参构造方法,determineConstructorsFromBeanPostProcessors(beanClass, beanName);这里面有一些逻辑,感兴趣的同学可以自己去看一看,如果没有找到有参构造方法,就返回默认的无参构造方法。到这里就返回了一个BeanWrapper对象。

4.代理bean,放入三级缓存

前面bean已经被实例化出来了,因为spring中有些对象需要被代理,来看看这段代码:

addSingleton.png

getEarlyBeanReference(beanName, mbd, bean));这个方法里,实际上就是去执行bean后置处理器中的方法,对bean进行代理:

Object proxy = createProxy(
      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
复制代码

最后会调用 ProxyFactory#getProxy 方法:

代理.png

这里是不是就是我们熟悉的JDK代理和Cglib代理~

然后看 addSingletonFactory() 这个方法:

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);
      }
   }
}
复制代码

这里就是将factory对象放入三级缓存中。

5.组装bean,循环依赖问题

这里就不贴代码了,感兴趣的同学下来再研究吧。这里就是对bean的属性进行填充,比如A依赖B,这里就是去填充B.

这里涉及到一个很经典的问题,就是spring中的循环依赖问题,spring到底是怎么解决循环依赖问题的呢?

我在读spring源码后,画了一张图,来清晰的表示:

循环依赖.png

假设A和B相互依赖,当创建A到了组装这一步骤时,需要去组装B,这个时候就会去getB,但是get不到,就会去创建B,当创建B到组装这一步骤时,需要去组装A,而A这个bean已经创建过了,并且在缓存中,可以得到A,这个时候B就组装完成了,而当B组装完成了,相应的A也组装完成。

最后A和B都是一个完整的bean,然后被放入一级缓存中,即单例池中。

到这里bean的整个创建过程和spring的循环依赖问题就讲完了,希望看到这篇文章的同学有所收获。

分类:
后端
标签:
分类:
后端
标签: