0原始碼基礎學習Spring原始碼系列(一)——Bean注入流程

京東雲開發者發表於2023-02-13

作者:京東科技 韓國凱

透過本文,讀者可以0原始碼基礎的初步學習spring原始碼,並能夠舉一反三從此進入原始碼世界的大米!
由於是第一次閱讀原始碼,文章之中難免存在一些問題,還望包涵指正!

一、 @Autowired與@Resource的區別

用一句話總結兩者的區別就是: @Autowired會先透過型別注入,即byType,當存在有多個型別時會透過名稱注入。@Resource則相反,會先透過名稱注入,即byName,當名稱不存在或有多個名稱時會透過型別注入。

那麼透過名稱注入與透過型別注入有什麼區別呢?

//建立介面
interface StuService{
    String getName();
}

@Service
//Stu2實現介面並註冊bean
class Stu2 implements StuService{
    @Override
    public String getName() {
        return "stu2";
    }
}

@Service
//Stu3實現介面並註冊bean
class Stu3 implements StuService{
    @Override
    public String getName() {
        return "stu3";
    }
}

1.1 @Autowired

那麼此時如果我們對 StuService注入, @Autowired可以選擇注入的型別就有兩個,分別是 Stu2與 Stu3

需要注意的是,型別有很多種選擇:

  1. 當註冊bean與獲取bean為同一個類時,型別只有這個類本身。

例如,我們有獲取session的工具類,需要將其注入到spring之中,

@Component
class SessionUtil{
    public String getSession(){
        return "session";
    }
}

只有一個類,直接註冊bean,使用時可以任意選擇

@Autowired
SessionUtil sessionUtil;

此時@Autowired只有一個註冊型別,直接注入。

  1. 當註冊bean有多個時,型別為所有註冊的bean,實現方式有:實現介面、繼承、透過其他方式,例如xml配置註冊bean。

例如上述 StuService有多個實現類,每個實現類都註冊了bean,因此@Autowired可以選擇的型別就有兩個。

@Autowired
StuService stu;

根據上述的@Autowired邏輯,此時有多個型別,那麼會根據bean name查詢,(即類名首字母小寫的),發現 stu沒有對應的實現類,

此時會報錯:

Field stu in com.example.demo.spring.Stu1 required a single bean, but 2 were found:

只需要將 stu 替換成 stu2或 stu3即可完成注入。

繼承和其他方式同時有多個bean注入時同理。

因此,@Autowired中型別的定義可以歸結為:當註冊bean有多個時,型別為所有註冊的bean,實現方式有:實現介面、繼承、透過其他方式,例如xml配置註冊bean或者@Bean註冊。

1.2 @Resource

  1. 當只有一個bean時,可以直接註冊
@Autowired
SessionUtil sessionUtil;
  1. 當有多個bean註冊時,如果未指定名稱,則bean name為類名首字母小寫,指定了bean名稱則註冊名稱為該名稱。

例如上文中 Stu1 Stu2都未指定bean名稱,因此兩者的bean名稱分別為 stu1 stu2

當使用@Bean在方法上註冊bean,此時名稱為方法名稱。

@Bean()
public Student getStudent(){
    Student student = new Student();
    student.setName("bob");
    student.setId(26);
    return student;
}

此時該bean名稱為 getStudent

同樣,我們也可以註冊bean時自定義bean名稱

@Bean("stu1")
public Student getStudent(){
    Student student = new Student();
    student.setName("bob");
    student.setId(26);
    return student;
}

@Service("stu2")
class Stu2 implements StuService{
    @Override
    public String getName() {
        return "stu2";
    }
}

@Component("stu3")
class Stu3 implements StuService{
    @Override
    public String getName() {
        return "stu3";
    }
}

在引用時指定bean:

@Resource(name = "stu2")
private StuService stu1;

1.3 @Autowired

當我們使用@Resource時,會根據名稱也就是 stu2去查詢,此時bean名稱只有一個,查到返回

@Resource
private Stu3 stu2;

但是在執行時卻發現報錯:

Bean named 'stu2' is expected to be of type 'com.example.demo.spring.Stu3' but was actually of type 'com.example.demo.spring.Stu2'

這是因為只根據了bean名稱去查詢,卻沒有根據bean型別,查到的是Stu2型別的bean,但是期望的卻是Stu3,因此會發生型別不匹配。

二、SpringIOC的Bean注入流程

spring的註冊流程主要包含兩個部分:

  1. 容器的啟動階段及預熱工作
  2. Bean的注入流程

先了解一下幾個概念:

2.1 概念介紹

2.1.1 配置後設資料

存在於磁碟上的專案中用於描述一個bean的資料,可以是xml、properties、yaml等靜態檔案,也可以是各種註解描述的對應資訊,例如@Service、@Component描述的一個bean的資訊。

<bean id="role" class="com.wbg.springxmlbean.entity.Role">
    <property name="id" value="1"/>
    <property name="roleName" value="高階工程師"/>
    <property name="note" value="重要人員"/>
</bean>

以上就是一個由xml定義的配置後設資料。

2.1.2 BeanDefinition與BeanDefinitionReader

在spring中,無論是那種配置後設資料,最終都會轉換為BeanDefinition,由BeanDefinition描述要生成並被引用的物件,可以理解為BeanDefinition就是bean的生成模板,或者是bean的說明書,按照BeanDefinition生成bean。

而將配置後設資料轉換為BeanDefinition的工作就是由BeanDefinitionReader完成的,對於不同的的配置後設資料有不同的Reader完成對應的工作,例如有XmlBeanDefinitionReader讀取xml配置資訊,PropertiesBeanDefinitionReader讀取properties配置資訊,AnnotatedBeanDefinitionReader讀取註解的配置資訊。

BeanDefinitionReader的作用就是將磁碟上的檔案資訊或註解資訊轉化為記憶體中用於描述bean的BeanDefinition。

2.1.3 BeanFactoryPostProcessor

BeanFactoryPostProcessor是容器啟動階段Spring提供的一個擴充套件點,主要負責對註冊到BeanDefinitionRegistry中的一個個的BeanDefinition進行一定程度上的修改與替換。例如我們的配置元資訊中有些可能會修改的配置資訊散落到各處,不夠靈活,修改相應配置的時候比較麻煩,這時我們可以使用佔位符的方式來配置。例如配置Jdbc的DataSource連線的時候可以這樣配置:

<bean id="dataSource"  
    class="org.apache.commons.dbcp.BasicDataSource"  
    destroy-method="close">  
    <property name="maxIdle" value="${jdbc.maxIdle}"></property>  
    <property name="maxActive" value="${jdbc.maxActive}"></property>  
    <property name="maxWait" value="${jdbc.maxWait}"></property>  
    <property name="minIdle" value="${jdbc.minIdle}"></property>  
  
    <property name="driverClassName"  
        value="${jdbc.driverClassName}">  
    </property>  
    <property name="url" value="${jdbc.url}"></property>  
  
    <property name="username" value="${jdbc.username}"></property>  
    <property name="password" value="${jdbc.password}"></property>  
</bean> 

BeanFactoryPostProcessor就會對註冊到BeanDefinitionRegistry中的BeanDefinition做最後的修改,替換$佔位符為配置檔案中的真實的資料。

2.1.4 BeanDefinitionRegistry

一個儲存BeanDefinition的地方,儲存方式為KV值,key為beanName,value為BeanDefinition。

2.1.5 容器啟動階段

容器的啟動階段相對比較簡單,首先會將存在於各處的磁碟上的配置元資訊由各自的Reader讀取到記憶體之中,轉換成BeanDefinition,然後註冊到BeanDefinationRegistry之中,最後由BeanFactoryPostProcessor進行修改與替換。

img

2.1.6 BeanFactory與FactoryBean

BeanFactory與FactoryBean的名字很像,但是確實兩個不同的東西。

根據命名規則來看,BeanFactory是一個Factory,也就是一個存放bean的工廠,在建立bean完成後放到其中,使用是從其中獲取。

而FactoryBean則是一個bean,只不過與不同的的bean不同的是他不僅可以建立本身型別的bean,也可以類似於Factory一樣建立一層有包裝的新的bean。這個Bean可以返回一個新的型別的bean,在返回之前也可以對其進行加工。

@Component
class FactoryBeanDemo implements FactoryBean<Student>{

    @Override
    public Student getObject() {
        return new Student();
    }

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

建立一個FactoryBean只需要實現其介面,並實現其中的兩個方法。當我們獲取FactoryBean時,會返回其中 getObject()方法返回的物件。而如果想要獲取FactoryBean本身,只需要在bean name前加一個"&"符號即可。

@Resource()
private Object factoryBeanDemo;

@GetMapping("/getStu")
private String getBean(){
    
    System.out.println(factoryBeanDemo.getClass());
    return stu2.getName();
}
//輸出結果
class com.example.demo.domain.Student

可以看到獲取到的是Student型別。

class com.example.demo.spring.FactoryBeanDemo

將獲取bean名稱假“&”符號:

@Resource(name = "&factoryBeanDemo")
private Object factoryBeanDemo;
class com.example.demo.spring.FactoryBeanDemo

可以看到獲取到的物件變成了FactoryBeanDemo本身。

2.2 Bean注入流程

在容器啟動階段,已經完成了bean的註冊。如果該物件是配置成懶載入的方式,那麼直到我們向Spring要依賴物件例項之前,其都是以BeanDefinitionRegistry中的一個個的BeanDefinition的形式存在,也就是Spring只有在我們第一次依賴物件的時候才開啟相應物件的例項化階段。而如果我們不是選擇懶載入的方式,容器啟動階段完成之後,其中有一個步驟finishBeanFactoryInitialization(),在這一步將立即啟動Bean例項化階段,透過隱式的呼叫所有依賴物件的getBean方法來例項化所有配置的Bean,完成類的載入。

doGetBean():獲取並返回bean

doGetBean()的主要流程有兩個:

  • 嘗試從快取中獲取bean,如果獲取到直接返回。
  • 如果沒有獲取到則嘗試載入bean。
protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {

   String beanName = transformedBeanName(name);
   Object beanInstance;

   // Eagerly check singleton cache for manually registered singletons.
   // 1、查詢快取中是否存在,存在的話直接返回
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      // 根據快取中的bean獲取例項,主要是檢測如果是FactoryBean型別,則獲取其內部的getObject()的bean。(需要先了解FactoryBean的作用)
      beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   //2、不存在則建立bean
   else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      // Check if bean definition exists in this factory.
      // 2.1 嘗試從父類的Factory載入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 if (requiredType != null) {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
         else {
            return (T) parentBeanFactory.getBean(nameToLookup);
         }
      }

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

      StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
            .tag("beanName", name);
      try {
         if (requiredType != null) {
            beanCreation.tag("beanType", requiredType::toString);
         }
         /*
         * 2.2 獲取RootBeanDefinition:首先會根據beanName獲取BeanDefinition,然後將BeanDefinition轉換為RootBeanDefinition
         * BeanDefinition 介面的實現類有很多,透過不同方式註冊到 BeanDefinitionRegistry 中的 BeanDefinition 的型別可能都不太相同。
           最終,在透過 BeanDefinition 來建立 bean 的例項時,通常都會呼叫 getMergedBeanDefinition 來獲取到一個 RootBeanDefinition。
           所以,RootBeanDefinition 本質上是 Spring 執行時統一的 BeanDefinition 檢視。
         * */
         RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // Guarantee initialization of beans that the current bean depends on.
         // 2.3 初始化依賴的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 {
                  getBean(dep);
               }
               catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
               }
            }
         }

         // Create bean instance.
         // 2.4 建立例項
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  //返回真正的bean
                  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;
               }
            });
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
   }

   return adaptBeanInstance(name, beanInstance, requiredType);
}

2.2.1 mbd = getMergedLocalBeanDefinition(beanName)獲取BeanDefinition

RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

BeanDefinition 介面的實現類有很多,透過不同方式註冊到 BeanDefinitionRegistry 中的 BeanDefinition 的型別可能都不太相同。
最終,在透過 BeanDefinition 來建立 bean 的例項時,通常都會呼叫 getMergedBeanDefinition 來獲取到一個 RootBeanDefinition。所以,RootBeanDefinition 本質上是 Spring 執行時統一的 BeanDefinition 檢視。

此處就是將各種BeanDefinition統一轉換為spring能識別的RootBeanDefinition。

2.2.2 getSingleton(String beanName, ObjectFactory<?> singletonFactory) 獲取建立好的物件

sharedInstance = getSingleton(beanName, () -> {
   try {
      //返回真正的bean
      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;
   }
});

getSingleton()方法中獲取建立好的物件

//獲取singletonFactory返回的結果
singletonObject = singletonFactory.getObject();

getSingleton()方法中最主要的一次呼叫也就是從singletonFactory中獲取物件,而獲取物件的結果就是上面程式碼中傳入的匿名工廠返回的結果,也就是 createBean(beanName, mbd, args)

2.2.3 createBean(beanName, mbd, args) 建立bean

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

   if (logger.isTraceEnabled()) {
      logger.trace("Creating instance of bean '" + beanName + "'");
   }
   RootBeanDefinition mbdToUse = mbd;

   // Make sure bean class is actually resolved at this point, and
   // clone the bean definition in case of a dynamically resolved Class
   // which cannot be stored in the shared merged bean definition.
   // 1.解析bean class
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }

   // Prepare method overrides.
   // 2.準備覆蓋的方法
   try {
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
            beanName, "Validation of method overrides failed", ex);
   }

   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      // 3.嘗試返回代理建立的Bean,這個作用就是查詢bean中所有實現前置和後置處理器的介面,有沒有手工建立然後返回的,代替了spring的建立bean的流程
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
      throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
            "BeanPostProcessor before instantiation of bean failed", ex);
   }

   try {
      //4.真正建立bean
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isTraceEnabled()) {
         logger.trace("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   }
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      // A previously detected exception with proper bean creation context already,
      // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
   }
}

建立bean主要有以下幾步:

  1. 解析bean的class檔案,為後面的根據class檔案透過反射建立物件做準備。
  2. 預處理bean的Override屬性,預處理的方式也比較簡單,就是在方法prepareMethodOverride中判斷一下,如果lookup-method標籤或者replaced-method標籤中配置了bean中需要覆蓋的方法,就將MethodOverride中的overload屬性值設定為false。
  3. 嘗試透過反射獲取被代理的bean。
  4. 真正建立bean的過程

2.2.4 Object beanInstance = doCreateBean(beanName, mbdToUse, args) 開始建立bean

以上流程都是獲取bean前的流程或獲取bean的準備,doCreateBean是真正的建立並填充bean的流程(去掉了一些不重要的程式碼)。

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

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      //1.透過反射建立例項化物件,並將其放入wraaper中。wraaper可以理解為bean的包裝物件,裡面是bean例項的,還有一些其他bean的屬性方便使用
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
   if (beanType != NullBean.class) {
      mbd.resolvedTargetType = beanType;
   }

   // Allow post-processors to modify the merged bean definition.
   //2.允許後處理處理器修改合併後的bean定義,這裡只是解析這些@Autowired @Value @Resource @PostConstruct等這些註解,並沒有發生實際屬性注入的動作
   synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
         try {
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         }
         mbd.postProcessed = true;
      }
   }

   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   //3.是否需要提前曝光,用來解決迴圈依賴時使用
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      if (logger.isTraceEnabled()) {
         logger.trace("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   //4.將例項化完成成的bean填充屬性
   populateBean(beanName, mbd, instanceWrapper);
   //5.呼叫初始化方法,例如 init-method
   exposedObject = initializeBean(beanName, exposedObject, mbd);
  
  
   //6.迴圈依賴檢查
   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
         }
      }
   }

   // Register bean as disposable.
   //7.註冊bean
   try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

從上述流程中可以看到,我們建立一個bean主要有以下幾個流程:

  1. 首先透過class根據反射建立物件,此時該物件的所有的屬性都為空,可以理解為我們new出的空屬性物件。
  2. 解析@Autowired @Value @Resource @PostConstruct這些註解,但並沒有發生屬性注入的行為。
  3. 是否需要提前曝光,用來解決迴圈依賴時使用,主要作用是如果需要代理會返回代理物件,如果不需要代理,返回前面建立的物件
  4. 將第一步例項化完成的空屬性物件填充屬性,其中如果該bean依賴了其他bean,也會在此步驟將依賴的bean裝配,如果bean已經被建立,則直接屬性注入,如果不存在,則建立bean,建立方式跟本bean相同,可以理解為遞迴。
  5. 將例項化完成的bean物件初始化,主要檢視bean是否實現了一些前置或後置或初始化的方法,如果是的話就執行。
  6. 迴圈依賴檢查。
  7. 根據scope註冊bean。

可以看到,經過以上的幾個步驟,我們就獲取到了一個例項bean。

其中最重要的三個方法:

  1. 例項化bean
  2. 裝配屬性
  3. 初始化bean

2.2.5 總結

總結來說,建立bean的流程就是先根據反射獲取物件,然後填充物件的屬性,初始化,最後將bean註冊。

在這裡插入圖片描述

2.3 建立bean流程深入理解

上文我們只粗略的講解了建立bean的過程,並沒有深入的檢視原始碼是如何實現的,例如透過反射獲取物件是怎麼獲取的,填充屬性是如何填充的,下文將詳細闡述2.2.5過程中在原始碼層面是如何構建的。

2.3.1 instanceWrapper = createBeanInstance(beanName, mbd, args) 獲取例項化物件

該方法透過反射獲取例項化的空屬性物件。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
   // Make sure bean class is actually resolved at this point.
   //1.1解析class
   Class<?> beanClass = resolveBeanClass(mbd, beanName);

   //1.2確認public許可權
   if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
   }

   //2.如果存在 Supplier 回撥,則呼叫 obtainFromSupplier() 進行初始化,因為反射獲取物件的效率比較低
   Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
   if (instanceSupplier != null) {
      return obtainFromSupplier(instanceSupplier, beanName);
   }

   if (mbd.getFactoryMethodName() != null) {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }

   // Shortcut when re-creating the same bean...
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         /*
         * 3.如果args為空且方法已經被resolved,則會直接選擇對應的構造方法
         * mbd.resolvedConstructorOrFactoryMethod的賦值在下方【1】【2】的程式碼中賦值
         * */
         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?
   //4.自動裝配的構造方法
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      return autowireConstructor(beanName, mbd, ctors, args);
   }

   // Preferred constructors for default construction?
   //5.是否有首選構造方法
   ctors = mbd.getPreferredConstructors();
   if (ctors != null) {
      return autowireConstructor(beanName, mbd, ctors, null);
   }

   // No special handling: simply use no-arg constructor.
   //6.透過預設的無參建構函式
   return instantiateBean(beanName, mbd);
}
  1. 首先解析class檔案與確認public許可權。
  2. 如果存在 Supplier 回撥,則呼叫 obtainFromSupplier() 進行初始化,因為反射獲取物件的效率比較低。
  3. 如果args為空且使用那個建構函式已經被確定了,則進行標記,後續直接選擇使用那種構造方法。
  4. 如果args不為空或沒有被解析過,則選擇使用那種構造方法來構造例項化的物件:

Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);

選擇AutowiredAnnotationBeanPostProcessor實現類:

其中重要的程式碼已貼出:

//1.遍歷所有的構造方法
for (Constructor<?> candidate : rawCandidates) {
   if (!candidate.isSynthetic()) {
      nonSyntheticConstructors++;
   }
   else if (primaryConstructor != null) {
      continue;
   }
   //2.檢視當前構造方法是否有@Autowired註解
   MergedAnnotation<?> ann = findAutowiredAnnotation(candidate);
   if (ann == null) {
      Class<?> userClass = ClassUtils.getUserClass(beanClass);
      if (userClass != beanClass) {
         try {
            Constructor<?> superCtor =
                  userClass.getDeclaredConstructor(candidate.getParameterTypes());
            ann = findAutowiredAnnotation(superCtor);
         }
         catch (NoSuchMethodException ex) {
            // Simply proceed, no equivalent superclass constructor found...
         }
      }
   }
   //3.如果有@Autowired註解
   if (ann != null) {
      //4.如果已經有一個@Autowired註解,則說明存在多個@Autowired註解,則丟擲異常
      if (requiredConstructor != null) {
         throw new BeanCreationException(beanName,
               "Invalid autowire-marked constructor: " + candidate +
               ". Found constructor with 'required' Autowired annotation already: " +
               requiredConstructor);
      }
      boolean required = determineRequiredStatus(ann);
      if (required) {
         if (!candidates.isEmpty()) {
            throw new BeanCreationException(beanName,
                  "Invalid autowire-marked constructors: " + candidates +
                  ". Found constructor with 'required' Autowired annotation: " +
                  candidate);
         }
         requiredConstructor = candidate;
      }
      candidates.add(candidate);
   }
   //5無參建構函式
   else if (candidate.getParameterCount() == 0) {
      //將其設定為預設建構函式
      defaultConstructor = candidate;
   }
}
//對上面的處理過程進行判斷
//6.1先檢查是否有@Autowired註解
if (!candidates.isEmpty()) {
   // Add default constructor to list of optional constructors, as fallback.
   if (requiredConstructor == null) {
      if (defaultConstructor != null) {
         candidates.add(defaultConstructor);
      }
      else if (candidates.size() == 1 && logger.isInfoEnabled()) {
         logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
               "': single autowire-marked constructor flagged as optional - " +
               "this constructor is effectively required since there is no " +
               "default constructor to fall back to: " + candidates.get(0));
      }
   }
  //返回@Autowired註解的構造方法
   candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
//6.2如果只有一個有參建構函式,則返回該有參函式
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
   candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
//6.3對於非Kotlin類只會返回null,所以這裡不會進入
else if (nonSyntheticConstructors == 2 && primaryConstructor != null &&
      defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {
   candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};
}
else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {
   candidateConstructors = new Constructor<?>[] {primaryConstructor};
}
else {
   //6.4對於不能識別的場景會進入到這裡,例如有多個建構函式但是並沒有指定@Autowired註解或者沒有建構函式(java會幫我們生成一個無參的建構函式),返回null
   candidateConstructors = new Constructor<?>[0];
}

2-5步會對所有的建構函式進行檢查,並在檢查完進行標記,並會在第6步對標記的結果進行返回,按照ifelse判斷順序主要分為以下幾種情況:

  • 如果有@Autowired註解的方法則返回該構造方法
  • 如果只有一個有參建構函式則會返回該有參建構函式
  • 對於不能識別的場景會進入到這裡,例如有多個建構函式但是並沒有指定@Autowired註解或者沒有建構函式(java會幫我們生成一個無參的建構函式)會返回null

在獲取到需要的建構函式後,會進行標記,下次不用再次解析可以直接選用那個建構函式,即上文的第4步

  1. 是否有首選的建構函式
  2. 如果都沒有的話,透過預設的無參建構函式建立物件。

我們檢視程式碼發現,無論第4步返回什麼結果,最終會執行以下兩個方法:

autowireConstructor()與instantiateBean()

兩者都會呼叫

instantiate()方法

最終都會執行以下這個方法

BeanUtils.instantiateClass(constructorToUse)

也就是如下的程式碼

for (int i = 0 ; i < args.length; i++) {
   if (args[i] == null) {
      Class<?> parameterType = parameterTypes[i];
      argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
   }
   else {
      argsWithDefaultValues[i] = args[i];
   }
}
return ctor.newInstance(argsWithDefaultValues);

其中最重要的一句:

return ctor.newInstance(argsWithDefaultValues);

可以發現,也就是這裡透過反射的方式建立了一個空屬性物件,並一層層返回,直到後面的屬性裝配等過程,可以說這裡就是bean載入過程的源頭。

2.3.2 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName) 解析各種註解

該方法主要解析該bean所相關的註解,例如屬性有@Resource,bean中@PostConstruct註解都會被解析。

for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
   processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}

processor主要有兩個實現類:

  1. AutowiredAnnotationBeanPostProcessor 處理@Autowired和@Value註解bean定義資訊
  2. CommonAnnotationBeanPostProcessor 處理@Resource、@PostConstruct、@PreDestroy註解的bean定義資訊

這裡需要注意的是,該方法只是會解析並不會真正的進行注入,因為學習意義不大,並不在贅述。

2.3.3 populateBean(beanName, mbd, instanceWrapper) 對例項化完成的bean進行屬性注入

//遍歷所有的屬性
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
   //對屬性進行裝填
   PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
   if (pvsToUse == null) {
      if (filteredPds == null) {
         filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      }
      pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
      if (pvsToUse == null) {
         return;
      }
   }
   pvs = pvsToUse;
}

其中 bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName)有幾個實現方法,比較重要的是:

  1. AutowiredAnnotationBeanPostProcessor,主要裝配屬性是@Autowired與@Value的屬性
  2. CommonAnnotationBeanPostProcessor,主要裝配屬性是@Resource的屬性

兩者最終都會進入如下方法:

//判斷要注入的是屬性還是方法
if (this.isField) {
   Field field = (Field) this.member;
   ReflectionUtils.makeAccessible(field);
   //如果是屬性的話則直接注入
   field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
   if (checkPropertySkipping(pvs)) {
      return;
   }
   try {
      Method method = (Method) this.member;
      ReflectionUtils.makeAccessible(method);
      //否則透過反射注入
      method.invoke(target, getResourceToInject(target, requestingBeanName));
   }
   catch (InvocationTargetException ex) {
      throw ex.getTargetException();
   }
}

理解起來比較簡單,判斷是方法注入還是屬性注入,在注入時注入的物件為:

getResourceToInject(target, requestingBeanName)

找到ResourceElement的實現方法中getResource()方法:

返回了 autowireResource(this.resourceFactory, element, requestingBeanName)

if (factory instanceof AutowireCapableBeanFactory) {
   AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
   DependencyDescriptor descriptor = element.getDependencyDescriptor();
   if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
      autowiredBeanNames = new LinkedHashSet<>();
      resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
      if (resource == null) {
         throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
      }
   }
   else {
      resource = beanFactory.resolveBeanByName(name, descriptor);
      autowiredBeanNames = Collections.singleton(name);
   }
}
else {
   resource = factory.getBean(name, element.lookupType);
   autowiredBeanNames = Collections.singleton(name);
}

在這個方法中,無論是if還是else,最終都會呼叫

getBean(name, element.lookupType)

也就是我們bean注入的入口,這個過程很像遞迴,在我們建立bean時,如果發現我們有依賴的其他bean,那麼就會去建立依賴的bean,如果依賴的bean還有其依賴的屬性則又會去建立被依賴的屬性,只到最終全部建立完成,返回一開始想要建立的bean。

2.3.4 exposedObject = initializeBean(beanName, exposedObject, mbd)初始化bean

在該方法中,會對已經填充過屬性的bean進行初始化:

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
   //對bean的前置處理,其中@PostConstruct就在此步驟中
   wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
   //呼叫初始化方法如果bean實現了InitializingBean介面,則先執行InitializingBean介面的afterPropertiesSet方法,然後執行xml或註解設定的init-method方法。
   invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
   throw new BeanCreationException(
         (mbd != null ? mbd.getResourceDescription() : null),
         beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
   //對bean進行後置處理,物件的代理發生在此步驟中
   wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

在初始化bean的時候,主要分為三個部分,分別是applyBeanPostProcessorsBeforeInitialization、invokeInitMethods、applyBeanPostProcessorsAfterInitialization,分別對應於初始化的前置處理、自定義init方法、後置處理。

applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization兩個方法的大概邏輯就是獲取獲取所有實現其介面的類,然後執行其中被覆蓋的方法。

常用的註解執行順序如下:

  1. @PostConstruct註解修飾的方法
  2. InitializingBean介面的afterPropertiesSet()方法
  3. init-method指定的方法
  4. @PreDestroy註解修飾的方法
  5. DisposableBean介面的destroy()方法
  6. destory-method指定的方法

並且在程式碼中可以看到,前置處理與後置處理都可以改變bean。

在容器啟動階段我們講到BeanFactoryPostProcessor,這裡我們講到BeanPostProcessor,那麼BeanFactoryPostProcessor 和 BeanPostProcessor 有什麼區別呢?

BeanFactoryPostProcessor存在於容器啟動階段,而BeanPostProcessor存在於物件例項化階段,BeanFactoryPostProcessor關注物件被建立之前那些配置的修改,而BeanPostProcessor階段關注物件已經被建立之後的功能增強,替換等操作,這樣就很容易區分了。
BeanPostProcessor與BeanFactoryPostProcessor都是Spring在Bean生產過程中強有力的擴充套件點。Spring中著名的AOP(面向切面程式設計),其實就是依賴BeanPostProcessor對Bean物件功能增強的。

BeanFactoryPostProcessor主要用於解決例項化之前,對例項的屬性進行擴充,而BeanPostProcessor是在例項化之後對物件做的擴充。

2.4 總結

用簡單的話描述一下,建立一個bean的過程大概包括三部分:

  1. 透過反射例項化bean
  2. 屬性裝配以及填充
  3. 初始化,包括init-method、以及其前後三個步驟。其中AOP增強就是發生在初始化之後的applyBeanPostProcessorsAfterInitialization的步驟中。

透過以上的步驟,就可以獲得我們可以正常使用的一個bean。

相關文章