談談Spring中的對象跟Bean,你知道Spring怎麼建立對象的嗎?


本系列文章:
java

讀源碼,咱們能夠從第一行讀起
程序員

你知道Spring是怎麼解析配置類的嗎?
web

配置類爲何要添加@Configuration註解?
spring

推薦閱讀:
數組

Spring官網閱讀 | 總結篇
緩存

Spring雜談
微信

本系列文章將會帶你一行行的將Spring的源碼吃透,推薦閱讀的文章是閱讀源碼的基礎!app

兩個問題

在開始探討源碼前,咱們先思考兩個問題:編輯器

一、在Spring中,什麼是Bean?跟對象有什麼區別?

經過new關鍵字,反射,克隆等手段建立出來的就是對象。在Spring中,Bean必定是一個對象,可是對象不必定是一個Bean,一個被建立出來的對象要變成一個Bean要通過不少複雜的工序,例如須要被咱們的BeanPostProcessor處理,須要通過初始化,須要通過AOPAOP自己也是由後置處理器完成的)等。ide

在這裏插入圖片描述

二、在建立對象前,Spring還作了其它什麼事情嗎?

咱們仍是回到流程圖中,其中相關的步驟以下:


在前面的三篇文章中,咱們已經分析到了第3-5步的源碼,而若是你對Spring源碼稍有了解的話,就是知道建立對象以及將對象變成一個Bean的過程發生在第3-11步驟中。中間的五步分別作了什麼呢?

一、registerBeanPostProcessors

就像名字所說的那樣,註冊BeanPostProcessor,這段代碼在Spring官網閱讀(八)容器的擴展點(三)(BeanPostProcessor)已經分析過了,因此在本文就直接跳過了,若是你沒有看過以前的文章也沒有關係,你只須要知道,在這裏Spring將全部的BeanPostProcessor註冊到了容器中

二、initMessageSource

初始化容器中的messageSource,若是程序員沒有提供,默認會建立一個org.springframework.context.support.DelegatingMessageSourceSpring官網閱讀(十一)ApplicationContext詳細介紹(上) 已經介紹過了。

三、initApplicationEventMulticaster

初始化事件分發器,若是程序員沒有提供,那麼默認建立一個org.springframework.context.event.ApplicationEventMulticaster,Spring官網閱讀(十二)ApplicationContext詳解(中)已經作過詳細分析,再也不贅述

四、onRefresh

留給子類複寫擴展使用

五、registerListeners

註冊事件監聽器,就是將容器中全部實現了org.springframework.context.ApplicationListener接口的對象放入到監聽器的集合中。

建立對象的源碼分析

在完成了上面的一些準備工做後,Spring開始來建立Bean了,按照流程,首先被調用的就是finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)方法,咱們就以這個方法爲入口,一步步跟蹤源碼,看看Spring中的Bean究竟是怎麼建立出來的,固然,本文主要關注的是建立對象的這個過程,對象變成Bean的流程咱們在後續文章中再分析

一、finishBeanFactoryInitialization

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // 初始化一個ConversionService用於類型轉換,這個ConversionService會在實例化對象的時候用到
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

  // 添加一個StringValueResolver,用於處理佔位符,能夠看到,默認狀況下就是使用環境中的屬性值來替代佔位符中的屬性
   if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
   }

   // 建立全部的LoadTimeWeaverAware
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, falsefalse);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // 靜態織入完成後將臨時的類加載器設置爲null,因此除了建立LoadTimeWeaverAware時可能會用到臨時類加載器,其他狀況下都爲空
   beanFactory.setTempClassLoader(null);

   // 將全部的配置信息凍結
   beanFactory.freezeConfiguration();

   // 開始進行真正的建立
   beanFactory.preInstantiateSingletons();
}

上面的方法最終調用了org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons來建立Bean。

其源碼以下:

二、preInstantiateSingletons

public void  preInstantiateSingletons() throws BeansException {
     // 全部bd的名稱 
  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
     // 遍歷全部bd,一個個進行建立 
  for (String beanName : beanNames) {
            // 獲取到指定名稱對應的bd
   RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            // 對不是延遲加載的單例的Bean進行建立
   if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                // 判斷是不是一個FactoryBean
    if (isFactoryBean(beanName)) {
                    // 若是是一個factoryBean的話,先建立這個factoryBean,建立factoryBean時,須要在beanName前面拼接一個&符號
     Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
     if (bean instanceof FactoryBean) {
      final FactoryBean<?> factory = (FactoryBean<?>) bean;
      boolean isEagerInit;
      if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
       isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
           ((SmartFactoryBean<?>) factory)::isEagerInit,
         getAccessControlContext());
      }
      else {
                            // 判斷是不是一個SmartFactoryBean,而且不是懶加載的,就意味着,在建立了這個factoryBean以後要立馬調用它的getObject方法建立另一個Bean
       isEagerInit = (factory instanceof SmartFactoryBean &&
         ((SmartFactoryBean<?>) factory).isEagerInit());
      }
      if (isEagerInit) {
       getBean(beanName);
      }
     }
    }
    else {
                    // 不是factoryBean的話,咱們直接建立就好了
     getBean(beanName);
    }
   }
  }
  // 在建立了全部的Bean以後,遍歷
  for (String beanName : beanNames) {
            // 這一步實際上是從緩存中獲取對應的建立的Bean,這裏獲取到的一定是單例的 
   Object singletonInstance = getSingleton(beanName);
            // 判斷是不是一個SmartInitializingSingleton,最典型的就是咱們以前分析過的EventListenerMethodProcessor,在這一步完成了對已經建立好的Bean的解析,會判斷其方法上是否有 @EventListener註解,會將這個註解標註的方法經過EventListenerFactory轉換成一個事件監聽器並添加到監聽器的集合中
   if (singletonInstance instanceof SmartInitializingSingleton) {
    final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
    if (System.getSecurityManager() != null) {
     AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
      smartSingleton.afterSingletonsInstantiated();
      return null;
     }, getAccessControlContext());
    }
    else {
     smartSingleton.afterSingletonsInstantiated();
    }
   }
  }
 }

上面這段代碼總體來講應該不難,不過它涉及到了一個點就是factoryBean,若是你對它不夠了解的話,請參考我以前的一篇文章:Spring官網閱讀(七)容器的擴展點(二)FactoryBean

三、doGetBean

從上面的代碼分析中咱們能夠知道,Spring最終都會調用到getBean方法,而getBean並非真正幹活的,doGetBean纔是。另外doGetBean能夠分爲兩種狀況

  • 建立的是一個 FactoryBean,此時實際傳入的 name = & + beanName
  • 建立的是一個普通Bean,此時傳入的 name = beanName

其代碼以下:

 protected <T> doGetBean(final String name, @Nullable final Class<T> requiredType,
   @Nullable final Object[] args, boolean typeCheckOnly)
 throws BeansException 
{
  // 前面咱們說過了,傳入的name可能時& + beanName這種形式,這裏作的就是去除掉&,獲得beanName
  final String beanName = transformedBeanName(name);
  Object bean;
  // 這個方法就很牛逼了,經過它解決了循環依賴的問題,不過目前咱們只須要知道它是從單例池中獲取已經建立的Bean便可,循環依賴後面我單獨寫一篇文章
        // 方法做用:已經建立的Bean會被放到單例池中,這裏就是從單例池中獲取
  Object sharedInstance = getSingleton(beanName);
        
  if (sharedInstance != null && args == null) {
            // 若是直接從單例池中獲取到了這個bean(sharedInstance),咱們能直接返回嗎?
            // 固然不能,由於獲取到的Bean多是一個factoryBean,若是咱們傳入的name是 & + beanName 這種形式的話,那是能夠返回的,可是咱們傳入的更多是一個beanName,那麼這個時候Spring就還須要調用這個sharedInstance的getObject方法來建立真正被須要的Bean
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }
  else {
            // 在緩存中獲取不到這個Bean
            // 原型下的循環依賴直接報錯
   if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
   }
            
            // 核心要義,找不到咱們就從父容器中再找一次
   BeanFactory parentBeanFactory = getParentBeanFactory();
   if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    String nameToLookup = originalBeanName(name);
    if (parentBeanFactory instanceof AbstractBeanFactory) {
     return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
       nameToLookup, requiredType, args, typeCheckOnly);
    }
    else if (args != null) {
     return (T) parentBeanFactory.getBean(nameToLookup, args);
    }
    else if (requiredType != null) {
     return parentBeanFactory.getBean(nameToLookup, requiredType);
    }
    else {
     return (T) parentBeanFactory.getBean(nameToLookup);
    }
   }
            
            // 若是不只僅是爲了類型推斷,也就是表明咱們要對進行實例化
            // 那麼就將bean標記爲正在建立中,其實就是將這個beanName放入到alreadyCreated這個set集合中
   if (!typeCheckOnly) {
    markBeanAsCreated(beanName);
   }
   try {
                
    final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                
                // 檢查合併後的bd是不是abstract,這個檢查如今已經沒有做用了,一定會經過
    checkMergedBeanDefinition(mbd, beanName, args);

    // @DependsOn註解標註的當前這個Bean所依賴的bean名稱的集合,就是說在建立當前這個Bean前,必需要先將其依賴的Bean先完成建立
    String[] dependsOn = mbd.getDependsOn();
    if (dependsOn != null) {
                    // 遍歷全部申明的依賴
     for (String dep : dependsOn) {
                        // 若是這個bean所依賴的bean又依賴了當前這個bean,出現了循環依賴,直接報錯
      if (isDependent(beanName, dep)) {
       throw new BeanCreationException(mbd.getResourceDescription(), beanName,
         "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
      }
                        // 註冊bean跟其依賴的依賴關係,key爲依賴,value爲依賴所從屬的bean
      registerDependentBean(dep, beanName);
      try {
                            // 先建立其依賴的Bean
       getBean(dep);
      }
      catch (NoSuchBeanDefinitionException ex) {
       throw new BeanCreationException(mbd.getResourceDescription(), beanName,
         "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
      }
     }
    }
    // 咱們目前只分析單例的建立,單例看懂了,原型天然就懂了
    if (mbd.isSingleton()) {
                    // 這裏再次調用了getSingleton方法,這裏跟方法開頭調用的getSingleton的區別在於,這個方法多傳入了一個ObjectFactory類型的參數,這個ObjectFactory會返回一個Bean
     sharedInstance = getSingleton(beanName, () -> {
      try {
       return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
       destroySingleton(beanName);
       throw ex;
      }
     });
     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
  // 省略原型跟域對象的相關代碼
  return (T) bean;
 }

配合註釋看這段代碼應該也不難吧,咱們重點關注最後在調用的這段方法便可

888

四、getSingleton(beanName,ObjectFactory)

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   synchronized (this.singletonObjects) {
       // 從單例池中獲取,這個地方確定也獲取不到
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
          // 工廠已經在銷燬階段了,這個時候還在建立Bean的話,就直接拋出異常
         if (this.singletonsCurrentlyInDestruction) {
            throw new BeanCreationNotAllowedException(beanName,
                  "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                  "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
         }
         // 在單例建立前,記錄一下正在建立的單例的名稱,就是把beanName放入到singletonsCurrentlyInCreation這個set集合中去
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
             // 這裏調用了singletonFactory的getObject方法,對應的實現就是在doGetBean中的那一段lambda表達式
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
        // 省略異常處理
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
             // 在單例完成建立後,將beanName從singletonsCurrentlyInCreation中移除
             // 標誌着這個單例已經完成了建立
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
             // 添加到單例池中
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

分析完上面這段代碼,咱們會發現,核心的建立Bean的邏輯就是在singletonFactory.getObject()這句代碼中,而其實現就是在doGetBean方法中的那一段lambda表達式,以下:

999

實際就是經過createBean這個方法建立了一個Bean而後返回,createBean又幹了什麼呢?

五、createBean

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

   RootBeanDefinition mbdToUse = mbd;
    
    // 解析獲得beanClass,爲何須要解析呢?若是是從XML中解析出來的標籤屬性確定是個字符串嘛
    // 因此這裏須要加載類,獲得Class對象
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }
   // 對XML標籤中定義的lookUp屬性進行預處理,若是隻能根據名字找到一個就標記爲非重載的,這樣在後續就不須要去推斷究竟是哪一個方法了,對於@LookUp註解標註的方法是不須要在這裏處理的,AutowiredAnnotationBeanPostProcessor會處理這個註解
   try {
      mbdToUse.prepareMethodOverrides();
   }
   // 省略異常處理...
   try {
       // 在實例化對象前,會通過後置處理器處理
       // 這個後置處理器的提供了一個短路機制,就是能夠提早結束整個Bean的生命週期,直接從這裏返回一個Bean
       // 不過咱們通常不會這麼作,它的另一個做用就是對AOP提供了支持,在這裏會將一些不須要被代理的Bean進行標記,就本文而言,你能夠暫時理解它沒有起到任何做用
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
    // 省略異常處理...
   try {
       // doXXX方法,真正幹活的方法,doCreateBean,真正建立Bean的方法
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isDebugEnabled()) {
         logger.debug("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   }
  // 省略異常處理...
}

六、doCreateBean

本文只探討對象是怎麼建立的,至於怎麼從一個對象變成了Bean,在後面的文章咱們再討論,因此咱們主要就關注下面這段代碼

// 這個方法真正建立了Bean,建立一個Bean會通過 建立對象 > 依賴注入 > 初始化 這三個過程,在這個過程當中,BeanPostPorcessor會穿插執行,本文主要探討的是建立對象的過程,因此關於依賴注入及初始化咱們暫時省略,在後續的文章中再繼續研究
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException 
{

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
       // 這行代碼看起來就跟factoryBean相關,這是什麼意思呢?
       // 在下文我會經過例子介紹下,你能夠暫時理解爲,這個地方返回的就是個null
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
       // 這裏真正的建立了對象
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   // 省略依賴注入,初始化
}

這裏我先分析下this.factoryBeanInstanceCache.remove(beanName)這行代碼。這裏須要說一句,我寫的這個源碼分析的系列很是的細節,之因此選擇這樣一個個扣細節是由於我本身在閱讀源碼過程當中常常會被這些問題阻塞,那麼藉着這些文章將本身踩過的坑分享出來能夠減小做爲讀者的你本身在閱讀源碼時的障礙,其次也可以提高本身閱讀源碼的能力。若是你對這些細節不感興趣的話,能夠直接跳過,能把握源碼的主線便可。言歸正傳,咱們回到這行代碼this.factoryBeanInstanceCache.remove(beanName)。何時factoryBeanInstanceCache這個集合中會有值呢?這裏我仍是以示例代碼來講明這個問題,示例代碼以下:

public class Main {
 public static void main(String[] args) {
  AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
 }
}

// 沒有作什麼特殊的配置,就是掃描了須要的組件,測試時換成你本身的包名
@ComponentScan("com.dmz.source.instantiation")
@Configuration
public class Config {
}

// 這裏申明瞭一個FactoryBean,而且經過@DependsOn註解申明瞭這個FactoryBean的建立要在orderService以後,主要目的是爲了在DmzFactoryBean建立前讓容器發生一次屬性注入
@Component
@DependsOn("orderService")
public class DmzFactoryBean implements FactoryBean<DmzService{
 @Override
 public DmzService getObject() throws Exception {
  return new DmzService();
 }

 @Override
 public Class<?> getObjectType() {
  return DmzService.class;
 }
}

// 沒有經過註解的方式將它放到容器中,而是經過上面的DmzFactoryBean來管理對應的Bean
public class DmzService {
}

// OrderService中須要注入dmzService
@Component
public class OrderService {
 @Autowired
 DmzService dmzService;
}

在這段代碼中,由於咱們明確的表示了DmzFactoryBean是依賴於orderService的,因此一定會先建立orderService再建立DmzFactoryBean,建立orderService的流程以下:

在這裏插入圖片描述

其中的屬性注入階段,咱們須要細化,也能夠畫圖以下:

在這裏插入圖片描述

爲orderService進行屬性注入能夠分爲這麼幾步

  1. 找到須要注入的注入點,也就是orderService中的dmzService字段

  2. 根據字段的類型以及名稱去容器中查詢符合要求的Bean

  3. 當遍歷到一個FactroyBean時,爲了肯定其getObject方法返回的對象的類型須要建立這個FactroyBean(只會到對象級別),而後調用這個建立好的FactroyBean的getObjectType方法明確其類型並與注入點須要的類型比較,看是不是一個候選的Bean,在建立這個FactroyBean時就將其放入了factoryBeanInstanceCache中。

  4. 在肯定了惟一的候選Bean以後,Spring就會對這個Bean進行建立,建立的過程又通過三個步驟

    在建立對象時,由於此時factoryBeanInstanceCache已經緩存了這個Bean對應的對象,因此直接經過this.factoryBeanInstanceCache.remove(beanName)這行代碼就返回了,避免了二次建立對象。

    • 建立對象
    • 屬性注入
    • 初始化

七、createBeanInstance

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   
   Class<?> beanClass = resolveBeanClass(mbd, beanName);
   // 省略異常
    
    // 經過bd中提供的instanceSupplier來獲取一個對象
    // 正常bd中都不會有這個instanceSupplier屬性,這裏也是Spring提供的一個擴展點,但實際上不經常使用
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   // bd中提供了factoryMethodName屬性,那麼要使用工廠方法的方式來建立對象,工廠方法又會區分靜態工廠方法跟實例工廠方法
   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }

   // 在原型模式下,若是已經建立過一次這個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, nullnull);
      }
      else {
         return instantiateBean(beanName, mbd);
      }
   }

   // 推斷構造函數
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }

   // 調用無參構造函數建立對象
   return instantiateBean(beanName, mbd);
}

上面這段代碼在Spring官網閱讀(一)容器及實例化 已經分析過了,可是當時咱們沒有深究建立對象的細節,因此本文將詳細探討Spring中的這個對象究竟是怎麼建立出來的,這也是本文的主題。

Spring官網閱讀(一)容器及實例化 這篇文章中,我畫了下面這麼一張圖

從上圖中咱們能夠知道Spring在實例化對象的時候有這麼幾種方式

  1. 經過bd中的supplier屬性
  2. 經過bd中的factoryMethodName跟factoryBeanName
  3. 經過構造函數

咱們接下來就一一分析其中的細節:

》經過bd中的supplier屬性實例化對象

Spring官網閱讀(一)容器及實例化 文中介紹過這種方式,由於這種方式咱們基本不會使用,並不重要,因此這裏就再也不贅述,我這裏就直接給出一個使用示例,你們自行體會吧

public static void main(String[] args) {
  AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    // 直接註冊一個Bean,而且指定它的supplier就是Service::new
  ac.registerBean("service", Service.class,Service::new,zhe'sh);
  ac.refresh();
  System.out.println(ac.getBean("service"));
}

》經過bd中的factoryMethodName跟factoryBeanName實例化對象

對應代碼以下:

protected BeanWrapper instantiateUsingFactoryMethod(
    String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs)
 
{
    return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}

上面這段代碼主要乾了兩件事

  • 建立一個 ConstructorResolver對象,從類名來看,它是一個構造器解析器
  • 調用了這個構造器解析器的 instantiateUsingFactoryMethod方法,這個方法見名知意,使用 FactoryMethod來完成實例化

基於此,咱們解決一個問題,ConstructorResolver是什麼?

ConstructorResolver是什麼?

在要研究一個類前,咱們最早應該從哪裏入手呢?不少沒有經驗的同窗可能會悶頭看代碼,可是實際上最好的學習方式是先閱讀類上的javaDoc

ConstructorResolver上的javaDoc以下:


上面這段javaDoc翻譯過來就是這個類就是用來解析構造函數跟工廠方法的代理者,而且它是經過參數匹配的方式來進行推斷構造方法或者工廠方法

看到這裏不知道小夥伴們是否有疑問,就是明明這個類不只負責推斷構造函數,還會負責推斷工廠方法,那麼爲何類名會叫作ConstructorResolver呢?咱們知道Spring的代碼在業界來講絕對是最規範的,沒有之一,這樣來講的話,這個類最合適的名稱應該是ConstructorAndFactoryMethodResolver纔對,由於它不只負責推斷了構造函數還負責推斷了工廠方法嘛!

這裏我須要說一下我本身的理解。對於一個Bean,它是經過構造函數完成實例化的,或者經過工廠方法實例化的,其實在這個Bean看來都沒有太大區別,這二者均可以稱之爲這個Bean的構造器,由於經過它們都能構造出一個Bean。因此Spring就把二者統稱爲構造器了,因此這個類名也就被稱爲ConstructorResolver了。

Spring在不少地方體現了這種實現,例如在XML配置的狀況下,不論咱們是使用構造函數建立對象仍是使用工廠方法建立對象,其參數的標籤都是使用constructor-arg。好比下面這個例子

<bean id="dmzServiceGetFromStaticMethod"
      factory-bean="factoryBean"
      factory-method="getObject">

    <constructor-arg type="java.lang.String" value="hello" name="s"/>
    <constructor-arg type="com.dmz.source.instantiation.service.DmzFactory" ref="factoryBean"/>
</bean>

<!--測試靜態工廠方法建立對象-->
<bean id="service"
      class="com.dmz.official.service.MyFactoryBean"
      factory-method="staticGet">

    <constructor-arg type="java.lang.String" value="hello"/>
</bean>

<bean id="dmzService" class="com.dmz.source.instantiation.service.DmzService">
    <constructor-arg name="s" value="hello"/>
</bean>

在對這個類有了大概的瞭解後,咱們就須要來分析它的源碼,這裏我就不把它單獨拎出來分析了,咱們藉着Spring的流程看看這個類幹了什麼事情

instantiateUsingFactoryMethod方法作了什麼?

核心目的:推斷出要使用的factoryMethod以及調用這個FactoryMethod要使用的參數,而後反射調用這個方法實例化出一個對象

這個方法的代碼太長了,因此咱們將它拆分紅爲一段一段的來分析

方法參數分析

在分析上面的代碼以前,咱們先來看看這個方法的參數都是什麼含義

方法上關於參數的介紹如圖所示


  • beanName:當前要實例化的Bean的名稱
  • mbd:當前要實例化的Bean對應的BeanDefinition
  • explicitArgs:這個參數在容器啓動階段咱們能夠認定它就是null,只有顯示的調用了getBean方法,而且傳入了明確的參數,例如: getBean("dmzService","hello")這種狀況下才會不爲null, 咱們分析這個方法的時候就直接認定這個參數爲null便可
第一段
public BeanWrapper instantiateUsingFactoryMethod(
    String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs)
 
{
    // 第一段代碼:建立並初始話一個BeanWrapperImpl
    BeanWrapperImpl bw = new BeanWrapperImpl();
    this.beanFactory.initBeanWrapper(bw);
    // ......
}

BeanWrapperImpl是什麼呢?若是你看過我以前的文章:Spring官網閱讀(十四)Spring中的BeanWrapper及類型轉換,那麼你對這個類應該不會陌生,它就是對Bean進行了一層包裝,而且在建立Bean的時候以及進行屬性注入的時候可以進行類型轉換。就算你沒看過以前的文章也不要緊,只要記住兩點

  • BeanWrapperImpl包裝了一個實例化好的對象
  • BeanWrapperImpl可以對屬性進行類型轉換

其層級關係以下:

回到咱們的源碼分析,咱們先來看看new BeanWrapperImpl()作了什麼事情?

對應代碼以下:

// 第一步:調用空參構造
public BeanWrapperImpl() {
    // 調用另一個構造函數,表示要註冊默認的屬性編輯器
    this(true);
}

// 這個構造函數代表是否要註冊默認編輯器,上面傳入的值爲true,表示須要註冊
public BeanWrapperImpl(boolean registerDefaultEditors) {
    super(registerDefaultEditors);
}

// 調用到父類的構造函數,肯定要使用默認的屬性編輯器
protected AbstractNestablePropertyAccessor(boolean registerDefaultEditors) {
    if (registerDefaultEditors) {
        registerDefaultEditors();
    }
    // 對typeConverterDelegate進行初始化
    this.typeConverterDelegate = new TypeConverterDelegate(this);
}

總的來講建立的過程很是簡單。第一,肯定要註冊默認的屬性編輯器;第二,對typeConverterDelegate屬性進行初始化。

緊接着,咱們看看在初始化這個BeanWrapper作了什麼?

// 初始化BeanWrapper,主要就是將容器中配置的conversionService賦值到當前這個BeanWrapper上
// 同時註冊定製的屬性編輯器
protected void initBeanWrapper(BeanWrapper bw) {
    bw.setConversionService(getConversionService());
    registerCustomEditors(bw);
}

還記得conversionService在何時被放到容器中的嗎?就是在finishBeanFactoryInitialization的時候啦~!

conversionService屬性完成賦值後就開始註冊定製的屬性編輯器,代碼以下:

// 傳入的參數就是咱們的BeanWrapper,它同時也是一個屬性編輯器註冊表
protected void registerCustomEditors(PropertyEditorRegistry registry) {
    PropertyEditorRegistrySupport registrySupport =
        (registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
    if (registrySupport != null) {
        // 這個配置的做用就是在註冊默認的屬性編輯器時,能夠增長對數組到字符串的轉換功能
        // 默認就是經過","來切割字符串轉換成數組,對應的屬性編輯器就是StringArrayPropertyEditor
        registrySupport.useConfigValueEditors();
    }
    // 將容器中的屬性編輯器註冊到當前的這個BeanWrapper
    if (!this.propertyEditorRegistrars.isEmpty()) {
        for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
            registrar.registerCustomEditors(registry);
            // 省略異常處理~
        }
    }
    // 這裏咱們沒有添加任何的自定義的屬性編輯器,因此確定爲空
    if (!this.customEditors.isEmpty()) {
        this.customEditors.forEach((requiredType, editorClass) ->
                                   registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
    }
}
第二段
public BeanWrapper instantiateUsingFactoryMethod(
    String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs)
 
{

    // 省略已經分析的第一段代碼,到這裏已經獲得了一個具備類型轉換功能的BeanWrapper
 
    // 實例化這個Bean的工廠Bean
    Object factoryBean;
    // 工廠Bean的Class
    Class<?> factoryClass;
    // 靜態工廠方法或者是實例化工廠方法
    boolean isStatic;
 
    /*下面這段代碼就是爲上面申明的這三個屬性賦值*/ 
    String factoryBeanName = mbd.getFactoryBeanName();
    // 若是建立這個Bean的工廠就是這個Bean自己的話,那麼直接拋出異常
    if (factoryBeanName != null) {
        if (factoryBeanName.equals(beanName)) {
            throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                                                   "factory-bean reference points back to the same bean definition");
        }
        // 獲得建立這個Bean的工廠Bean
        factoryBean = this.beanFactory.getBean(factoryBeanName);
        if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
            throw new ImplicitlyAppearedSingletonException();
        }
        factoryClass = factoryBean.getClass();
        isStatic = false;
    }
    else {
        // factoryBeanName爲null,說明是經過靜態工廠方法來實例化Bean的
        // 靜態工廠進行實例化Bean,beanClass屬性必需要是工廠的class,若是爲空,直接報錯
        if (!mbd.hasBeanClass()) {
            throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                                                   "bean definition declares neither a bean class nor a factory-bean reference");
        }
        factoryBean = null;
        factoryClass = mbd.getBeanClass();
        isStatic = true;
    }
    // 省略後續代碼
}

小總結:

這段代碼很簡單,就是確認實例化當前這個Bean的工廠方法是靜態工廠仍是實例工廠,若是是實例工廠,那麼找出對應的工廠Bean。

第三段
public BeanWrapper instantiateUsingFactoryMethod(
    String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs)
 
{
    
    // 省略第一段,第二段代碼
    
    // 到這裏已經獲得了一個BeanWrapper,明確了實例化當前這個Bean究竟是靜態工廠仍是實例工廠
    // 而且已經肯定了工廠Bean
    
    // 最終肯定的要用來建立對象的方法
    Method factoryMethodToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    Object[] argsToUse = null;
 
    // 參數分析時已經說過,explicitArgs就是null
    if (explicitArgs != null) {
        argsToUse = explicitArgs;
    }
    else {
        // 下面這段代碼是什麼意思呢?
        // 在原型模式下,咱們會屢次建立一個Bean,因此Spring對參數以及所使用的方法作了緩存
        // 在第二次建立原型對象的時候會進入這段緩存的邏輯
        // 可是這裏有個問題,爲何Spring對參數有兩個緩存呢?
        // 一:resolvedConstructorArguments
        // 二:preparedConstructorArguments
        // 這裏主要是由於,直接使用解析好的構造的參數,由於這樣會致使建立出來的全部Bean都引用同一個屬性
        Object[] argsToResolve = null;
        synchronized (mbd.constructorArgumentLock) {
            factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
            // 緩存已經解析過的工廠方法或者構造方法
            if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
                // resolvedConstructorArguments跟preparedConstructorArguments都是對參數的緩存
                argsToUse = mbd.resolvedConstructorArguments;
                if (argsToUse == null) {
                    argsToResolve = mbd.preparedConstructorArguments;
                }
            }
        }
        if (argsToResolve != null) {
            // preparedConstructorArguments須要再次進行解析
            argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);
        }
    }
    // 省略後續代碼
}

小總結:

上面這段代碼應該沒什麼大問題,其核心思想就是從緩存中取已經解析出來的方法以及參數,這段代碼只會在原型模式下生效,由於單例的話對象只會建立一次嘛~!最大的問題在於,爲何在對參數進行緩存的時候使用了兩個不一樣的集合,而且緩存後的參數還須要再次解析,這個問題咱們暫且放着,不妨帶着這個問題往下看。


由於接下來要分析的代碼就比較複雜了,因此爲了讓你完全看到代碼的執行流程,下面我會使用示例+流程圖+文字的方式來分析源碼。

示例代碼以下(這個例子覆蓋接下來要分析的全部流程):

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
    default-autowire="constructor">
<!--這裏開啓自動注入,而且是經過構造函數進行自動注入-->

 <!--factoryObject 提供了建立對象的方法-->
 <bean id="factoryObject" class="com.dmz.spring.first.instantiation.service.FactoryObject"/>

 <!--提供一個用於測試自動注入的對象-->
 <bean class="com.dmz.spring.first.instantiation.service.OrderService" id="orderService"/>
 
    <!--主要測試這個對象的實例化過程-->
 <bean id="dmzService" factory-bean="factoryObject" factory-method="getDmz" scope="prototype">
  <constructor-arg name="name" value="dmz"/>
  <constructor-arg name="age" value="18"/>
  <constructor-arg name="birthDay" value="2020-05-23"/>
 </bean>
 
    <!--測試靜態方法實例化對象的過程-->
 <bean id="indexService" class="com.dmz.spring.first.instantiation.service.FactoryObject"
    factory-method="staticGetIndex"/>

 
    <!--提供這個轉換器,用於轉換dmzService中的birthDay屬性,從字符串轉換成日期對象-->
 <bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="conversionService">
  <property name="converters">
   <set>
    <bean class="com.dmz.spring.first.instantiation.service.ConverterStr2Date"/>
   </set>
  </property>
 </bean>
</beans>

測試代碼:

public class FactoryObject {

 public DmzService getDmz(String name, int age, Date birthDay, OrderService orderService) {
  System.out.println("getDmz with "+"name,age,birthDay and orderService");
  return new DmzService();
 }

 public DmzService getDmz(String name, int age, Date birthDay) {
  System.out.println("getDmz with "+"name,age,birthDay");
  return new DmzService();
 }

 public DmzService getDmz(String name, int age) {
  System.out.println("getDmz with "+"name,age");
  return new DmzService();
 }

 public DmzService getDmz() {
  System.out.println("getDmz with empty arg");
  return new DmzService();
 }

 public static IndexService staticGetIndex() {
  return new IndexService();
 }
}

public class DmzService {
}

public class IndexService {
}

public class OrderService {
}

public class ConverterStr2Date implements Converter<StringDate{
 @Override
 public Date convert(String source) {
  try {
   return new SimpleDateFormat("yyyy-MM-dd").parse(source);
  } catch (ParseException e) {
   return null;
  }
 }
}

/**
 * @author 程序員DMZ
 * @Date Create in 23:14 2020/5/21
 * @Blog https://daimingzhi.blog.csdn.net/
 */

public class Main {
 public static void main(String[] args) {
  ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext();
  cc.setConfigLocation("application.xml");
  cc.refresh();
  cc.getBean("dmzService");
        // 兩次調用,用於測試緩存的方法及參數
//  cc.getBean("dmzService");

 }
}

運行上面的代碼會發現,程序打印:

getDmz with name,age,birthDay and orderService

具體緣由我相信你看了接下來的源碼分析天然就懂了

第四段
public BeanWrapper instantiateUsingFactoryMethod(
    String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs)
 
{
//  第一段代碼:到這裏已經獲得了一個BeanWrapper,並對這個BeanWrapper作了初始化
//  第二段代碼:明確了實例化當前這個Bean究竟是靜態工廠仍是實例工廠
// 第三段代碼:以及從緩存中取過了對應了方法以及參數

// 進入第四段代碼分析,執行到這段代碼說明是第一次實例化這個對象
if (factoryMethodToUse == null || argsToUse == null) {
   // 若是被cglib代理的話,獲取父類的class
   factoryClass = ClassUtils.getUserClass(factoryClass);
   // 獲取到工廠類中的全部方法,接下來要一步步從這些方法中篩選出來符合要求的方法
   Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
   List<Method> candidateList = new ArrayList<>();
      // 第一步篩選:以前 在第二段代碼中已經推斷了方法是靜態或者非靜態的
      // 因此這裏第一個要求就是要知足靜態/非靜態這個條件
      // 第二個要求就是必須符合bd中定義的factoryMethodName的名稱
      // 其中第二個要求請注意,若是bd是一個configurationClassBeanDefinition,也就是說是經過掃描@Bean註解產生的,那麼在判斷時還會添加是否標註了@Bean註解
   for (Method candidate : rawCandidates) {
    if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
     candidateList.add(candidate);
    }
   }
      // 將以前獲得的方法集合轉換成數組
      // 到這一步獲得的其實就是某一個方法的全部重載方法
       // 好比dmz(),dmz(String name),dmz(String name,int age)
   Method[] candidates = candidateList.toArray(new Method[0]);
    
      // 排序,public跟參數多的優先級越高
   AutowireUtils.sortFactoryMethods(candidates);
   
      // 用來保存從配置文件中解析出來的參數
   ConstructorArgumentValues resolvedValues = null;
            // 是否使用了自動注入,本段代碼中沒有使用到這個屬性,可是在後面用到了
   boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
   int minTypeDiffWeight = Integer.MAX_VALUE;
      // 可能出現多個符合要求的方法,用這個集合保存,實際上若是這個集合有值,就會拋出異常了
   Set<Method> ambiguousFactoryMethods = null;

   int minNrOfArgs;
      // 一定爲null,不考慮了
   if (explicitArgs != null) {
    minNrOfArgs = explicitArgs.length;
   }
   else {
                // 就是說配置文件中指定了要使用的參數,那麼須要對其進行解析,解析後的值就存儲在resolvedValues這個集合中
    if (mbd.hasConstructorArgumentValues()) {
                    // 經過解析constructor-arg標籤,將參數封裝成了ConstructorArgumentValues
                    // ConstructorArgumentValues這個類在下文咱們專門分析
     ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
     resolvedValues = new ConstructorArgumentValues();
                    // 解析標籤中的屬性,相似進行類型轉換,後文進行詳細分析
     minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
    }
    else {
                    // 配置文件中沒有指定要使用的參數,因此執行方法的最小參數個數就是0
     minNrOfArgs = 0;
    }
   }
 // 省略後續代碼....
}

小總結:

由於在實例化對象前一定要先肯定具體要使用的方法,因此這裏先作的第一件事就是肯定要在哪一個範圍內去推斷要使用的factoryMethod呢?

最大的範圍就是這個factoryClass的全部方法,也就是源碼中的rawCandidates

其次須要在rawCandidates中進一步作推斷,由於在前面第二段代碼的時候已經肯定了是靜態方法仍是非靜態方法,而且BeanDefinition也指定了factoryMethodName,那麼基於這兩個條件這裏就須要對rawCandidates進一步進行篩選,獲得一個candidateList集合。

咱們對示例的代碼進行調試會發現

image-20200523205856655

確實如咱們所料,rawCandidates是factoryClass中的全部方法,candidateList是全部getDmz的重載方法。

在肯定了推斷factoryMethod的範圍後,那麼接下來要根據什麼去肯定到底使用哪一個方法呢?換個問題,怎麼區分這麼些重載的方法呢?確定是根據方法參數嘛!

因此接下來要作的就是去解析要使用的參數了~

對於Spring而言,方法的參數會分爲兩種

  1. 配置文件中指定的
  2. 自動注入模式下,須要去容器中查找的

在上面的代碼中,Spring就是將配置文件中指定的參數作了一次解析,對應方法就是resolveConstructorArguments

在查看這個方法的源碼前,咱們先看看ConstructorArgumentValues這個類

public class ConstructorArgumentValues {
 // 經過下標方式指定的參數
 private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<>();
 // 沒有指定下標
 private final List<ValueHolder> genericArgumentValues = new ArrayList<>();
 // 省略無關代碼.....
}

在前文的註釋中咱們也說過了,它主要的做用就是封裝解析constructor-arg標籤獲得的屬性,解析標籤對應的方法就是org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseConstructorArgElement,這個方法我就不帶你們看了,有興趣的能夠自行閱讀。

它主要有兩個屬性

  1. indexedArgumentValues
  2. genericArgumentValues

對應的就是咱們兩種指定參數的方法,以下:

<bean id="dmzService" factory-bean="factoryObject" factory-method="getDmz" scope="prototype">
    <constructor-arg name="name" value="dmz"/>
    <constructor-arg name="age" value="18"/>
    <constructor-arg index="2"  value="2020-05-23"/>
    <!--  <constructor-arg name="birthDay" value="2020-05-23"/>-->
</bean>

其中的name跟age屬性會被解析爲genericArgumentValues,而index=2會被解析爲indexedArgumentValues

在對ConstructorArgumentValues有必定認知以後,咱們再來看看resolveConstructorArguments的代碼:

// 方法目的:解析配置文件中指定的方法參數
// beanName:bean名稱
// mbd:beanName對應的beanDefinition
// bw:經過它進行類型轉換
// ConstructorArgumentValues cargs:解析標籤獲得的屬性,尚未通過解析(類型轉換)
// ConstructorArgumentValues resolvedValues:已經通過解析的參數
// 返回值:返回方法須要的最小參數個數
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
                                        ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues)
 
{
 
    // 是否有定製的類型轉換器,沒有的話直接使用BeanWrapper進行類型轉換
    TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
    TypeConverter converter = (customConverter != null ? customConverter : bw);
    
    // 構造一個BeanDefinitionValueResolver,專門用於解析constructor-arg中的value屬性,實際上還包括ref屬性,內嵌bean標籤等等
    BeanDefinitionValueResolver valueResolver =
        new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
 
    // minNrOfArgs 記錄執行方法要求的最小參數個數,通常狀況下就是等於constructor-arg標籤指定的參數數量
    int minNrOfArgs = cargs.getArgumentCount();

    for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
        int index = entry.getKey();
        if (index < 0) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                            "Invalid constructor argument index: " + index);
        }
        // 這是啥意思呢?
        // 這個代碼我認爲是有問題的,而且我給Spring官方已經提了一個issue,官方將會在5.2.7版本中修復
        // 暫且你先這樣理解
        // 假設A方法直接在配置文件中指定了index=3上要使用的參數,那麼這個時候A方法至少須要4個參數
        // 可是其他的3個參數可能不是經過constructor-arg標籤指定的,而是直接自動注入進來的,那麼在配置文件中咱們就只配置了index=3上的參數,也就是說 int minNrOfArgs = cargs.getArgumentCount()=1,這個時候 index=3,minNrOfArgs=1, 因此 minNrOfArgs = 3+1
        if (index > minNrOfArgs) {
            minNrOfArgs = index + 1;
        }
        ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
        // 若是已經轉換過了,直接添加到resolvedValues集合中
        if (valueHolder.isConverted()) {
            resolvedValues.addIndexedArgumentValue(index, valueHolder);
        }
        else {
            // 解析value/ref/內嵌bean標籤等
            Object resolvedValue =
                valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
            // 將解析後的resolvedValue封裝成一個新的ValueHolder,並將其source設置爲解析constructor-arg獲得的那個ValueHolder,後期會用到這個屬性進行判斷
            ConstructorArgumentValues.ValueHolder resolvedValueHolder =
                new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
            resolvedValueHolder.setSource(valueHolder);
            resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
        }
    }
   // 對getGenericArgumentValues進行解析,代碼基本同樣,再也不贅述
    return minNrOfArgs;
}

能夠看到,最終的解析邏輯就在resolveValueIfNecessary這個方法中,那麼這個方法又作了什麼呢?

// 這個方法的目的就是將解析constructor-arg標籤獲得的value值進行一次解析
// 在解析標籤時ref屬性會被封裝爲RuntimeBeanReference,那麼在這裏進行解析時就會去調用getBean
// 在解析value屬性會會被封裝爲TypedStringValue,那麼這裏會嘗試去進行一個轉換
// 關於標籤的解析你們有興趣的話能夠去看看org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parsePropertyValue
// 這裏再也不贅述了
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
 
    // 解析constructor-arg標籤中的ref屬性,實際就是調用了getBean
    if (value instanceof RuntimeBeanReference) {
        RuntimeBeanReference ref = (RuntimeBeanReference) value;
        return resolveReference(argName, ref);
    }
    
    // ......
    
       /**  
   * <constructor-arg>
   *    <set value-type="java.lang.String">
   *     <value>1</value>
   *    </set>
   * </constructor-arg>
   * 經過上面set標籤中的value-type屬性對value進行類型轉換,
   * 若是value-type屬性爲空,那麼這裏不會進行類型轉換
   */

   else if (value instanceof TypedStringValue) {
   TypedStringValue typedStringValue = (TypedStringValue) value;
   Object valueObject = evaluate(typedStringValue);
   try {
    Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
    if (resolvedTargetType != null) {
     return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
    }
    else {
     return valueObject;
    }
   }
   catch (Throwable ex) {
    // Improve the message by showing the context.
    throw new BeanCreationException(
      this.beanDefinition.getResourceDescription(), this.beanName,
      "Error converting typed String value for " + argName, ex);
   }
  }
    // 省略後續代碼....
}

就咱們上面的例子而言,通過resolveValueIfNecessary方法並不能產生實際的影響,由於在XML中咱們沒有配置ref屬性或者value-type屬性。

畫圖以下:

image-20200529071428155
第五段
public BeanWrapper instantiateUsingFactoryMethod(
    String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs)
 
{
    //  第一段代碼:到這裏已經獲得了一個BeanWrapper,並對這個BeanWrapper作了初始化
    //  第二段代碼:明確了實例化當前這個Bean究竟是靜態工廠仍是實例工廠
    // 第三段代碼:以及從緩存中取過了對應了方法以及參數
    //  第四段代碼:明確了方法須要的最小的參數數量並對配置文件中的標籤屬性進行了一次解析

    // 進入第五段代碼分析
    
    // 保存在建立方法參數數組過程當中發生的異常,若是最終沒有找到合適的方法,那麼將這個異常信息封裝後拋出
    LinkedList<UnsatisfiedDependencyException> causes = null;
    
    // 開始遍歷全部在第四段代碼中查詢到的符合要求的方法
    for (Method candidate : candidates) {
        // 方法的參數類型
        Class<?>[] paramTypes = candidate.getParameterTypes();
        // 候選的方法的參數必需要大於在第四段這推斷出來的最小參數個數
        if (paramTypes.length >= minNrOfArgs) {
            ArgumentsHolder argsHolder;
            // 一定爲null,不考慮
            if (explicitArgs != null) {
                // Explicit arguments given -> arguments length must match exactly.
                if (paramTypes.length != explicitArgs.length) {
                    continue;
                }
                argsHolder = new ArgumentsHolder(explicitArgs);
            }
            else {
                // Resolved constructor arguments: type conversion and/or autowiring necessary.
                try {
                    // 獲取參數的具體名稱
                    String[] paramNames = null;
                    ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                    if (pnd != null) {
                        paramNames = pnd.getParameterNames(candidate);
                    }
                    // 根據方法的參數名稱以及配置文件中配置的參數建立一個參數數組用於執行工廠方法
                    argsHolder = createArgumentArray(
                        beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring);
                }
                // 在建立參數數組的時候可能發生異常,這個時候的異常不能直接拋出,要確保全部的候選方法遍歷完成,只要有一個方法符合要求便可,可是若是遍歷完全部方法仍是沒找到合適的構造器,那麼直接拋出這些異常
                catch (UnsatisfiedDependencyException ex) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
                    }
                    // Swallow and try next overloaded factory method.
                    if (causes == null) {
                        causes = new LinkedList<>();
                    }
                    causes.add(ex);
                    continue;
                }
                // 計算類型差別
                // 首先判斷bd中是寬鬆模式仍是嚴格模式,目前看來只有@Bean標註的方法解析獲得的Bean會使用嚴格模式來計算類型差別,其他都是使用寬鬆模式
                // 嚴格模式下,
                int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                                      argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
                // 選擇一個類型差別最小的方法
                if (typeDiffWeight < minTypeDiffWeight) {
                    factoryMethodToUse = candidate;
                    argsHolderToUse = argsHolder;
                    argsToUse = argsHolder.arguments;
                    minTypeDiffWeight = typeDiffWeight;
                    ambiguousFactoryMethods = null;
                }
    // 省略後續代碼.......
    }

小總結:這段代碼的核心思想就是根據第四段代碼從配置文件中解析出來的參數構造方法執行所須要的實際參數數組。若是構建成功就表明這個方法能夠用於實例化Bean,而後計算實際使用的參數跟方法上申明的參數的」差別值「,並在全部符合要求的方法中選擇一個差別值最小的方法

接下來,咱們來分析方法實現的細節

  1. 構建方法使用的參數數組,也就是 createArgumentArray方法,其源碼以下:
/* beanName:要實例化的Bean的名稱
 * mbd:對應Bean的BeanDefinition
 * resolvedValues:從配置文件中解析出來的並嘗試過類型轉換的參數
 * bw:在這裏主要就是用做類型轉換器
 * paramTypes:當前遍歷到的候選的方法的參數類型數組
 * paramNames:當前遍歷到的候選的方法的參數名稱
 * executable:當前遍歷到的候選的方法
 * autowiring:是否時自動注入
 */

private ArgumentsHolder createArgumentArray(
    String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
   BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
   boolean autowiring)
 throws UnsatisfiedDependencyException 
{

  TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
  TypeConverter converter = (customConverter != null ? customConverter : bw);

  ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
  Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
  Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
  // 遍歷候選方法的參數,跟據方法實際須要的類型到resolvedValues中去匹配
  for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
   Class<?> paramType = paramTypes[paramIndex];
   String paramName = (paramNames != null ? paramNames[paramIndex] : "");
   
   ConstructorArgumentValues.ValueHolder valueHolder = null;
   if (resolvedValues != null) {
                // 首先,根據方法參數的下標到resolvedValues中找對應的下標的屬性
                // 若是沒找到再根據方法的參數名/類型去resolvedValues查找
    valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
    // 若是都沒找到
                // 1.是自動注入而且方法的參數長度正好跟配置中的參數數量相等
                // 2.不是自動注入
                // 那麼按照順序一次選取
    if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) {
     valueHolder = resolvedValues.getGenericArgumentValue(nullnull, usedValueHolders);
    }
   }
   // 也就是說在配置的參數中找到了合適的值能夠應用於這個方法上
   if (valueHolder != null) {
    // 防止同一個參數被應用了屢次
    usedValueHolders.add(valueHolder);
    Object originalValue = valueHolder.getValue();
    Object convertedValue;
                // 已經進行過類型轉換就不會須要再次進行類型轉換
    if (valueHolder.isConverted()) {
     convertedValue = valueHolder.getConvertedValue();
     args.preparedArguments[paramIndex] = convertedValue;
    }
    else {
     // 嘗試將配置的值轉換成方法參數須要的類型
     MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
     try {
                        // 進行類型轉換
      convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam);
     }
     catch (TypeMismatchException ex) {
      // 拋出UnsatisfiedDependencyException,在調用該方法處會被捕獲
     }
     Object sourceHolder = valueHolder.getSource();
                    // 只要是valueHolder存在,到這裏這個判斷一定成立
     if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
      Object sourceValue = ((ConstructorArgumentValues.ValueHolder) sourceHolder).getValue();
      args.resolveNecessary = true;
      args.preparedArguments[paramIndex] = sourceValue;
     }
    }

    args.arguments[paramIndex] = convertedValue;
    args.rawArguments[paramIndex] = originalValue;
   }
   else {
                // 方法執行須要參數,可是resolvedValues中沒有提供這個參數,也就是說這個參數是要自動注入到Bean中的
    MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
    // 不是自動注入,直接拋出異常
    if (!autowiring) {
     // 拋出UnsatisfiedDependencyException,在調用該方法處會被捕獲
    }
    try {
                    // 自動注入的狀況下,調用getBean獲取須要注入的Bean
     Object autowiredArgument =
       resolveAutowiredArgument(methodParam, beanName, autowiredBeanNames, converter);
                    // 把getBean返回的Bean封裝到本次方法執行時須要的參數數組中去
     args.rawArguments[paramIndex] = autowiredArgument;
     args.arguments[paramIndex] = autowiredArgument;
                    // 標誌這個參數是自動注入的
     args.preparedArguments[paramIndex] = new AutowiredArgumentMarker();
                    // 自動注入的狀況下,在第二次調用時,須要從新處理,不能直接緩存
     args.resolveNecessary = true;
    }
    catch (BeansException ex) {
     // 拋出UnsatisfiedDependencyException,在調用該方法處會被捕獲
    }
   }
  }
  
     // 註冊Bean之間的依賴關係
  for (String autowiredBeanName : autowiredBeanNames) {
   this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
   if (logger.isDebugEnabled()) {
    logger.debug("Autowiring by type from bean name '" + beanName +
      "' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
      " to bean named '" + autowiredBeanName + "'");
   }
  }

  return args;
 }

上面這段代碼說難也難,說簡單也簡單,若是要完全看懂它到底幹了什麼仍是頗有難度的。簡單來講,它就是從第四段代碼解析出來的參數中查找當前的這個候選方法須要的參數。若是找到了,那麼嘗試對其進行類型轉換,將其轉換成符合方法要求的類型,若是沒有找到那麼還須要判斷當前方法的這個參數能不能進行自動注入,若是能夠自動注入的話,那麼調用getBean獲得須要的Bean,並將其注入到方法須要的參數中。

第六段
public BeanWrapper instantiateUsingFactoryMethod(
    String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs)
 
{
    //  第一段代碼:到這裏已經獲得了一個BeanWrapper,並對這個BeanWrapper作了初始化
    //  第二段代碼:明確了實例化當前這個Bean究竟是靜態工廠仍是實例工廠
    // 第三段代碼:以及從緩存中取過了對應了方法以及參數
    //  第四段代碼:明確了方法須要的最小的參數數量並對配置文件中的標籤屬性進行了一次解析
    //  第五段代碼:到這裏已經肯定了可使用來實例化Bean的方法是哪一個
    
 // 省略拋出異常的代碼,就是在對推斷出來的方法作驗證
    // 1.推斷出來的方法不能爲null
    // 2.推斷出來的方法返回值不能爲void
    // 3.推斷出來的方法不能有多個
  
    // 對參數進行緩存
      if (explicitArgs == null && argsHolderToUse != null) {
         argsHolderToUse.storeCache(mbd, factoryMethodToUse);
      }
   }

   try {
      Object beanInstance;

      if (System.getSecurityManager() != null) {
         final Object fb = factoryBean;
         final Method factoryMethod = factoryMethodToUse;
         final Object[] args = argsToUse;
         beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
               beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, beanFactory, fb, factoryMethod, args),
               beanFactory.getAccessControlContext());
      }
      else {
          // 反射調用對應方法進行實例化
          // 1.獲取InstantiationStrategy,主要就是SimpleInstantiationStrategy跟CglibSubclassingInstantiationStrategy,其中CglibSubclassingInstantiationStrategy主要是用來處理beanDefinition中的lookupMethod跟replaceMethod。一般來講咱們使用的就是SimpleInstantiationStrateg
          // 2.SimpleInstantiationStrateg就是單純的經過反射調用方法
         beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
               mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);
      }
  // beanWrapper在這裏對Bean進行了包裝
      bw.setBeanInstance(beanInstance);
      return bw;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Bean instantiation via factory method failed", ex);
   }
}

上面這段代碼的主要目的就是

  1. 緩存參數,原型可能屢次建立同一個對象
  2. 反射調用推斷出來的factoryMethod

》經過構造函數實例化對象

若是上面你對使用factoryMethd進行實例化對象已經足夠了解的話,那麼下面的源碼分析基本沒有什麼很大區別,咱們接着看看代碼。

首先,咱們回到createBeanInstance方法中,

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // 上面的代碼已經分析過了
    // 1.使用supplier來獲得一個對象
    // 2.經過factotryMethod方法實例化一個對象
 
    // 看起來是否是有點熟悉,在使用factotryMethod建立對象時也有差很少這樣的一段代碼,看起來就是使用緩存好的方法直接建立一個對象
    boolean resolved = false;
    boolean autowireNecessary = false;
    
    // 不對這個參數進行討論,就認爲一直爲null
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            // bd中的resolvedConstructorOrFactoryMethod不爲空,說明已經解析過構造方法了
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                // resolved標誌是否解析過構造方法
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    if (resolved) {
        // 構造函數已經解析過了,而且這個構造函數在調用時須要自動注入參數
        if (autowireNecessary) {
            // 此時部分解析好的參數已經存在了beanDefinition中,而且構造函數也在bd中
            // 那麼在這裏只會從緩存中去取構造函數以及參數而後反射調用
            return autowireConstructor(beanName, mbd, nullnull);
        }
        else {
            // 這裏就是直接反射調用空參構造
            return instantiateBean(beanName, mbd);
        }
    }
 // 推斷出可以使用的須要參數的構造函數
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    // 在推斷出來的構造函數中選取一個合適的方法來進行Bean的實例化
    // ctors不爲null:說明存在1個或多個@Autowired標註的方法
    // mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR:說明是自動注入
    // mbd.hasConstructorArgumentValues():配置文件中配置了構造函數要使用的參數
    // !ObjectUtils.isEmpty(args):外部傳入的參數,一定爲null,很少考慮
    // 上面的條件只要知足一個就會進入到autowireConstructor方法
    // 第一個條件知足,那麼經過autowireConstructor在推斷出來的構造函數中再進一步選擇一個差別值最小的,參數最長的構造函數
    // 第二個條件知足,說明沒有@Autowired標註的方法,可是須要進行自動注入,那麼經過autowireConstructor會去遍歷類中申明的全部構造函數,並查找一個差別值最小的,參數最長的構造函數
    // 第三個條件知足,說明不是自動注入,那麼要經過配置中的參數去類中申明的全部構造函數中匹配
    // 第四個一定爲null,不考慮
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
        mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }
 
    // 反射調用空參構造
    return instantiateBean(beanName, mbd);
}

由於autowireConstructor方法的執行邏輯跟instantiateUsingFactoryMethod方法的執行邏輯基本一致,只是將Method對象換成了Constructor對象,因此對這個方法我再也不作詳細的分析。

咱們主要就看看determineConstructorsFromBeanPostProcessors這個方法吧,這個方法的主要目的就是推斷出候選的構造方法。

determineConstructorsFromBeanPostProcessors方法作了什麼?

// 實際調用的就是AutowiredAnnotationBeanPostProcessor中的determineCandidateConstructors方法
// 這個方法看起來很長,但實際確很簡單,就是經過@Autowired註解肯定哪些構造方法能夠做爲候選方法,其實在使用factoryMethod來實例化對象的時候也有這種邏輯在其中,後續在總結的時候咱們對比一下
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
   throws BeanCreationException {

  // 這裏作的事情很簡單,就是將@Lookup註解標註的方法封裝成LookupOverride添加到BeanDefinition中的methodOverrides屬性中,若是這個屬性不爲空,在實例化對象的時候不能選用SimpleInstantiationStrateg,而要使用CglibSubclassingInstantiationStrategy,經過cglib代理給方法加一層攔截了邏輯
     // 避免重複檢查
  if (!this.lookupMethodsChecked.contains(beanName)) {
   try {
    ReflectionUtils.doWithMethods(beanClass, method -> {
     Lookup lookup = method.getAnnotation(Lookup.class);
     if (lookup != null) {
      Assert.state(this.beanFactory != null"No BeanFactory available");    // 將@Lookup註解標註的方法封裝成LookupOverride
      LookupOverride override = new LookupOverride(method, lookup.value());
      try {
                            // 添加到BeanDefinition中的methodOverrides屬性中
       RootBeanDefinition mbd = (RootBeanDefinition)
         this.beanFactory.getMergedBeanDefinition(beanName);
       mbd.getMethodOverrides().addOverride(override);
      }
      catch (NoSuchBeanDefinitionException ex) {
       throw new BeanCreationException(beanName,
         "Cannot apply @Lookup to beans without corresponding bean definition");
      }
     }
    });
   }
   catch (IllegalStateException ex) {
    throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
   }
   this.lookupMethodsChecked.add(beanName);
  }
  
    // 接下來要開始肯定到底哪些構造函數能被做爲候選者
  
    // 先嚐試從緩存中獲取
  Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
  if (candidateConstructors == null) {
   // Fully synchronized resolution now...
   synchronized (this.candidateConstructorsCache) {
    candidateConstructors = this.candidateConstructorsCache.get(beanClass);、
                    // 緩存中沒法獲取到,進入正式的推斷過程
    if (candidateConstructors == null) {
     Constructor<?>[] rawCandidates;
     try {
                        // 第一步:先查詢這個類全部的構造函數,包括私有的
      rawCandidates = beanClass.getDeclaredConstructors();
     }
     catch (Throwable ex) {
      // 省略異常信息
     }
     List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
                    // 保存添加了Autowired註解而且required屬性爲true的構造方法
     Constructor<?> requiredConstructor = null;
                    // 空參構造
     Constructor<?> defaultConstructor = null;
                    // 看方法註釋上說明的,這裏除非是kotlin的類,不然一定爲null,不作過多考慮,咱們就將其看成null
     Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
     int nonSyntheticConstructors = 0;
                    // 對類中的全部構造方法進行遍歷
     for (Constructor<?> candidate : rawCandidates) {
                        // 非合成方法
      if (!candidate.isSynthetic()) {
       nonSyntheticConstructors++;
      }
      else if (primaryConstructor != null) {
       continue;
      }
                        // 查詢方法上是否有Autowired註解
      AnnotationAttributes ann = findAutowiredAnnotation(candidate);
      if (ann == null) {
                            // userClass != beanClass說明這個類進行了cglib代理
       Class<?> userClass = ClassUtils.getUserClass(beanClass);
       if (userClass != beanClass) {
        try {
                                    // 若是進行了cglib代理,那麼在父類上再次查找Autowired註解
         Constructor<?> superCtor =
           userClass.getDeclaredConstructor(candidate.getParameterTypes());
         ann = findAutowiredAnnotation(superCtor);
        }
        catch (NoSuchMethodException ex) {
         // Simply proceed, no equivalent superclass constructor found...
        }
       }
      }
                        // 說明當前的這個構造函數上有Autowired註解
      if (ann != null) {
       if (requiredConstructor != null) {
        // 省略異常拋出
       }
                            // 獲取Autowired註解中的required屬性
       boolean required = determineRequiredStatus(ann);
       if (required) {
                                // 類中存在多個@Autowired標註的方法,而且某個方法的@Autowired註解上被申明瞭required屬性要爲true,那麼直接報錯
        if (!candidates.isEmpty()) {
        // 省略異常拋出
        }
        requiredConstructor = candidate;
       }
                            // 添加到集合中,這個集合存儲的都是被@Autowired註解標註的方法
       candidates.add(candidate);
      }
                        // 空參構造函數
      else if (candidate.getParameterCount() == 0) {
       defaultConstructor = candidate;
      }
     }
     if (!candidates.isEmpty()) {
      // 存在多個被@Autowired標註的方法
                        // 而且全部的required屬性被設置成了false (默認爲true)
      if (requiredConstructor == null) {
                            // 存在空參構造函數,注意,空參構造函數能夠不被@Autowired註解標註
       if (defaultConstructor != null) {
                                // 將空參構造函數也加入到候選的方法中去
        candidates.add(defaultConstructor);
       }
       // 省略日誌打印
      }
                        
      candidateConstructors = candidates.toArray(new Constructor<?>[0]);
     }
                    // 也就是說,類中只提供了一個構造函數,而且這個構造函數不是空參構造函數
     else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
      candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
     }
                    // 省略中間兩個判斷,primaryConstructor一定爲null,不考慮
     // .....
     }
     else {
                        // 說明沒法推斷出來
      candidateConstructors = new Constructor<?>[0];
     }
     this.candidateConstructorsCache.put(beanClass, candidateConstructors);
    }
   }
  }
  return (candidateConstructors.length > 0 ? candidateConstructors : null);
 }

這裏我簡單總結下這個方法的做用

  1. 獲取到類中的全部構造函數

  2. 查找到被@Autowired註解標註的構造函數

    • 若是存在多個被 @Autowired標註的構造函數,而且其required屬性沒有被設置爲true,那麼返回這些被標註的函數的集合(空參構造即便沒有添加 @Autowired也會被添加到集合中)
    • 若是存在多個被 @Autowired標註的構造函數,而且其中一個的required屬性被設置成了true,那麼直接報錯
    • 若是隻有一個構造函數被 @Autowired標註,而且其required屬性被設置成了true,那麼直接返回這個構造函數
  3. 若是沒有被@Autowired標註標註的構造函數,可是類中有且只有一個構造函數,而且這個構造函數不是空參構造函數,那麼返回這個構造函數

  4. 上面的條件都不知足,那麼determineCandidateConstructors這個方法就沒法推斷出合適的構造函數了

能夠看到,經過AutowiredAnnotationBeanPostProcessordetermineCandidateConstructors方法能夠處理構造函數上的@Autowired註解。

可是,請注意,這個方法並不能決定到底使用哪一個構造函數來建立對象(即便它只推斷出來一個,也不必定可以使用),它只是經過@Autowired註解來肯定構造函數的候選者,在構造函數都沒有添加@Autowired註解的狀況下,這個方法推斷不出來任何方法。真正肯定到底使用哪一個構造函數是交由autowireConstructor方法來決定的。前文已經分析過了instantiateUsingFactoryMethod方法,autowireConstructor的邏輯基本跟它一致,因此這裏再也不作詳細的分析。

factoryMethod跟構造函數的比較

總體邏輯比較

image-20200601074832836

從上圖中能夠看到,總體邏輯上它們並無什麼區別,只是查找的對象從factoryMethod換成了構造函數

執行細節比較

細節的差別主要體如今推斷方法上

  • 推斷factoryMethod
image-20200601221449783
  • 推斷構造函數
image-20200601225630816

它們之間的差別我已經在圖中標識出來了,主要就是兩點

  1. 經過構造函數實例化對象,多了一層處理,就是要處理構造函數上的@Autowired註解以及方法上的@LookUp註解(要決定選取哪種實例化策略, SimpleInstantiationStrategy/ CglibSubclassingInstantiationStrategy
  2. 在最終的選取也存在差別,對於facotyMehod而言,在寬鬆模式下( 除ConfigurationClassBeanDefinition外,也就是掃描@Bean獲得的BeanDefinition,都是寬鬆模式),會選取一個最精準的方法,在嚴格模式下,會選取一個參數最長的方法
  3. 對於構造函數而言,會一定會選取一個參數最長的方法

關於計算類型差別的補充內容

思考了好久,我仍是決定再補充一些內容,就是關於上面兩幅圖的最後一步,對應的核心代碼以下:

int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                      argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));

if (typeDiffWeight < minTypeDiffWeight) {
    factoryMethodToUse = candidate;
    argsHolderToUse = argsHolder;
    argsToUse = argsHolder.arguments;
    minTypeDiffWeight = typeDiffWeight;
    ambiguousFactoryMethods = null;
}

判斷bd是嚴格模式仍是寬鬆模式,上面說過不少次了,bd默認就是寬鬆模式,只要在ConfigurationClassBeanDefinition中使用嚴格模式,也就是掃描@Bean標註的方法註冊的bd(對應的代碼能夠參考:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod方法)

咱們再看看嚴格模式跟寬鬆模式在計算差別值時的區別

public int getTypeDifferenceWeight(Class<?>[] paramTypes) {
    // 計算實際使用的參數跟方法申明的參數的差別值
   int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments);
    // 計算沒有通過類型轉換的參數跟方法申明的參數的差別值
   int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024;
   return (rawTypeDiffWeight < typeDiffWeight ? rawTypeDiffWeight : typeDiffWeight);
  }

public static int getTypeDifferenceWeight(Class<?>[] paramTypes, Object[] args) {
    int result = 0;
    for (int i = 0; i < paramTypes.length; i++) {
        // 在出現類型轉換時,下面這個判斷纔會成立,也就是在比較rawArguments跟paramTypes的差別時纔可能知足這個條件
        if (!ClassUtils.isAssignableValue(paramTypes[i], args[i])) {
            return Integer.MAX_VALUE;
        }
        if (args[i] != null) {
            Class<?> paramType = paramTypes[i];
            Class<?> superClass = args[i].getClass().getSuperclass();
            while (superClass != null) {
                // 若是咱們傳入的值是方法上申明的參數的子類,那麼每多一層繼承關係,差別值加2
                if (paramType.equals(superClass)) {
                    result = result + 2;
                    superClass = null;
                }
                else if (ClassUtils.isAssignable(paramType, superClass)) {
                    result = result + 2;
                    superClass = superClass.getSuperclass();
                }
                else {
                    superClass = null;
                }
            }
            // 判斷方法的參數是否是一個接口,若是是,那麼差別值加1
            if (paramType.isInterface()) {
                result = result + 1;
            }
        }
    }
    return result;
}
public int getAssignabilityWeight(Class<?>[] paramTypes) {
    // 嚴格模式下,只有三種返回值
    // 1.Integer.MAX_VALUE,通過類型轉換後仍是不符合要求,返回最大的類型差別
    // 由於解析後的參數可能返回一個NullBean(建立對象的方法返回了null,Spring會將其包裝成一個NullBean),不過通常不會出現這種狀況,因此咱們能夠當這種狀況不存在
   for (int i = 0; i < paramTypes.length; i++) {
    if (!ClassUtils.isAssignableValue(paramTypes[i], this.arguments[i])) {
     return Integer.MAX_VALUE;
    }
   }
    // 2.Integer.MAX_VALUE - 512,進行過了類型轉換才符合要求
   for (int i = 0; i < paramTypes.length; i++) {
    if (!ClassUtils.isAssignableValue(paramTypes[i], this.rawArguments[i])) {
     return Integer.MAX_VALUE - 512;
    }
   }
    // 3.Integer.MAX_VALUE - 1024,沒有通過類型轉換就已經符合要求了,返回最小的類型差別
   return Integer.MAX_VALUE - 1024;
  }

首先,無論是factoryMethod仍是constructor,都是採用上面的兩個方法來計算類型差別,可是正常來講,只有factoryMethod會採用到嚴格模式(除非程序員手動干預,好比經過Bean工廠後置處理器修改了bd中的屬性,這樣一般來講沒有很大意義)

因此咱們分爲三種狀況討論

一、factoryMethod+寬鬆模式

這種狀況下,會選取一個最精確的方法,同時方法的參數要儘可能長

測試代碼:

public class FactoryObject {

 public DmzService getDmz() {
  System.out.println(0);
  return new DmzService();
 }

 public DmzService getDmz(OrderService indexService) {
  System.out.println(1);
  return new DmzService();
 }

 public DmzService getDmz(OrderService orderService, IndexService indexService) {
  System.out.println(2);
  return new DmzService();
 }

    public DmzService getDmz(OrderService orderService, IndexService indexService,IA ia) {
        System.out.println(3);
        return new DmzService();
    }
}

public class ServiceImpl implements IService {
}

public class IAImpl implements IA {
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor">
<!--必需要開啓自動注入,而且是經過構造函數進行自動注入,不然選用無參構造-->

 <!--factoryObject 提供了建立對象的方法-->
 <bean id="factoryObject" class="com.dmz.spring.instantiation.service.FactoryObject"/>

 <bean class="com.dmz.spring.instantiation.service.OrderService" id="orderService"/>

 <bean id="dmzService" factory-bean="factoryObject" factory-method="getDmz" />

 <bean class="com.dmz.spring.instantiation.service.ServiceImpl" id="iService"/>

 <bean class="com.dmz.spring.instantiation.service.IndexService" id="indexService"/>

</beans>
/**
 * @author 程序員DMZ
 * @Date Create in 23:59 2020/6/1
 * @Blog https://daimingzhi.blog.csdn.net/
 */

public class XMLMain {
 public static void main(String[] args) {
  ClassPathXmlApplicationContext cc =
    new ClassPathXmlApplicationContext("application.xml");
 }
}

運行程序發現,選用了第三個(getDmz(OrderService orderService, IndexService indexService))構造方法。雖然最後一個方法的參數更長,可是由於其方法申明的參數上存在接口,因此它的差別值會大於第三個方法,由於不會被選用

二、factoryMethod+嚴格模式

這種狀況下,會選取一個參數儘可能長的方法

測試代碼:

/**
 * @author 程序員DMZ
 * @Date Create in 6:28 2020/6/1
 * @Blog https://daimingzhi.blog.csdn.net/
 */

@ComponentScan("com.dmz.spring.instantiation")
@Configuration
public class Config {

 @Bean
 public DmzService dmzService() {
  System.out.println(0);
  return new DmzService();
 }
 @Bean
 public DmzService dmzService(OrderService indexService) {
  System.out.println(1);
  return new DmzService();
 }
 @Bean
 public DmzService dmzService(OrderService orderService, IndexService indexService) {
  System.out.println(2);
  return new DmzService();
 }
 @Bean
 public DmzService dmzService(OrderService orderService, IndexService indexService, IA ia) {
  System.out.println("config " +3);
  return new DmzService();
 }

 @Bean
 public DmzService dmzService(OrderService orderService, IndexService indexService, IA ia, IService iService) {
  System.out.println("config " +4);
  return new DmzService();
 }
}

/**
 * @author 程序員DMZ
 * @Date Create in 6:29 2020/6/1
 * @Blog https://daimingzhi.blog.csdn.net/
 */

public class Main {
 public static void main(String[] args) {
  AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  ac.register(Config.class);
  ac.refresh();
 }
}

運行程序,發現選用了最後一個構造函數,這是由於在遍歷候選方法時,會先遍歷參數最長的,而在計算類型差別時,由於嚴格模式下,上面全部方法的差別值都是同樣的,都會返回Integer.MAX_VALUE - 1024。實際上,在不進行手動干預的狀況下,都會返滬這個值。

三、構造函數+寬鬆模式

這種狀況下,也會選取一個參數儘可能長的方法

之因此會這樣,主要是由於在autowireConstructor方法中進行了一次短路判斷,以下所示:


在上圖中,若是已經找到了合適的方法,那麼直接就不會再找了,而在遍歷的時候是從參數最長的方法開始遍歷的,測試代碼以下:

@Component
public class DmzService {
 
    // 沒有添加@Autowired註解,也會被看成候選方法
 public DmzService(){
  System.out.println(0);
 }

 @Autowired(required = false)
 public DmzService(OrderService orderService) {
  System.out.println(1);
 }

 @Autowired(required = false)
 public DmzService(OrderService orderService, IService iService) {
  System.out.println(2);
 }

 @Autowired(required = false)
 public DmzService(OrderService orderService, IndexService indexService, IService iService,IA ia) {
  System.out.println("DmzService "+3);
 }
}

/**
 * @author 程序員DMZ
 * @Date Create in 6:29 2020/6/1
 * @Blog https://daimingzhi.blog.csdn.net/
 */

public class Main {
 public static void main(String[] args) {
  AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
  ac.register(Config.class);
  ac.refresh();
 }
}

這篇文章就到這裏啦~~!

文章很長,但願你耐心看完,碼字不易,若是有幫助到你的話點個贊吧~!

Spring官網閱讀筆記

Spring雜談

JVM系列文章

Spring源碼專題



本文分享自微信公衆號 - 程序員DMZ(programerDmz)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索