Spring原始碼:Bean的生命週期(二)

2023-05-02 06:00:42

前言

讓我們繼續講解Spring的Bean範例化過程。在上一節中,我們已經講解了Spring是如何將Bean定義加入到IoC容器中,並使用合併的Bean定義來包裝原始的Bean定義。接下來,我們將繼續講解Spring的 getBean() 方法,特別是針對 FactoryBean 的解析。

getBean() 方法中,Spring還支援對 FactoryBean 進行特殊處理。FactoryBean 是一個能夠生成Bean範例的工廠Bean,其定義了 getObject() 方法,返回的是一個由工廠Bean管理的物件範例。在使用 getBean() 方法獲取 FactoryBean 型別的Bean時,Spring會首先獲取 FactoryBean 的範例,然後呼叫其 getObject() 方法來獲取由工廠Bean建立的實際Bean範例。

因此,在使用 getBean() 方法獲取Bean範例時,我們需要注意是否需要對 FactoryBean 進行特殊處理。如果需要獲取 FactoryBean 的範例而不是它所管理的物件範例,可以在Bean名稱前加上 & 符號來進行標識。例如:&myFactoryBean 表示獲取 myFactoryBean 的範例。但是博主看到第一篇原始碼寫的篇幅確實有些長,可能對於大傢伙的碎片化時間掌握的不是很充分,所以以後我會盡力控制篇幅長度,既保證邏輯的連續性也保證儘快可以看完,那麼接下來開始進入正題getbean方法之FactoryBean解析。

FactoryBean

所有符合過濾條件的Bean在Spring解析後都會被轉化為合併後的Bean定義。儘管Spring提供了 getBean() 方法用於獲取Bean範例,但實際上它底層仍然使用 createBean() 方法來建立Bean範例。在建立Bean範例之前,Spring先對當前Bean定義進行判斷,以確定其是否為 FactoryBean 型別:

public void preInstantiateSingletons() throws BeansException {  
   if (logger.isTraceEnabled()) {  
      logger.trace("Pre-instantiating singletons in " + this);  
   }  
  
   // Iterate over a copy to allow for init methods which in turn register new bean definitions.  
 // While this may not be part of the regular factory bootstrap, it does otherwise work fine.  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);  
  
   // Trigger initialization of all non-lazy singleton beans...  
  for (String beanName : beanNames) {  
      // 獲取合併後的BeanDefinition  
  RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);  
  
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {  
         if (isFactoryBean(beanName)) {  
            // 獲取FactoryBean物件  
  Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);  
            if (bean instanceof FactoryBean) {  
               FactoryBean<?> factory = (FactoryBean<?>) bean;  
               boolean isEagerInit;  
               if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {  
                  isEagerInit = AccessController.doPrivileged(  
                        (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,  
                        getAccessControlContext());  
               }  
               else {  
                  isEagerInit = (factory instanceof SmartFactoryBean &&  
                        ((SmartFactoryBean<?>) factory).isEagerInit());  
               }  
               if (isEagerInit) {  
                  // 建立真正的Bean物件(getObject()返回的物件)  
  getBean(beanName);  
               }  
            }  
         }  
         else {  
            // 建立Bean物件  
  getBean(beanName);  
         }  
      }  
   }  
  
   // 所有的非懶載入單例Bean都建立完了後  
  // Trigger post-initialization callback for all applicable beans...  
  for (String beanName : beanNames) {  
      Object singletonInstance = getSingleton(beanName);  
      if (singletonInstance instanceof SmartInitializingSingleton) {  
         StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")  
               .tag("beanName", beanName);  
         SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;  
         if (System.getSecurityManager() != null) {  
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {  
               smartSingleton.afterSingletonsInstantiated();  
               return null;  
            }, getAccessControlContext());  
         }  
         else {  
            smartSingleton.afterSingletonsInstantiated();  
         }  
         smartInitialize.end();  
      }  
   }  
}

他的原始碼邏輯大致如下:

  1. Spring會根據 beanNamebean 定義 Map 中獲取當前合併的 Bean 定義。
  2. Spring會對當前 Bean 定義進行判斷,包括判斷當前 Bean 是否為抽象的、是否為單例、是否懶載入,以及是否為 FactoryBean。如果是 FactoryBean,則會走 FactoryBean 的建立邏輯,否則會走單例 Bean 的建立邏輯。
  3. 當所有單例非懶載入的 Bean 建立完成後,Spring會遍歷所有單例 Bean,判斷其是否為 SmartInitializingSingleton 型別。如果是,則會自動呼叫 afterSingletonsInstantiated 方法。

isFactoryBean

由於建立 Bean 的邏輯比較複雜,其中包含了許多細節,因此,在這裡我們特別提到了一個方法 isFactoryBean()。之所以要提到這個方法,是因為Spring支援使用 FactoryBean 來建立複雜物件。下面是該方法的主要原始碼:

public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {  
   String beanName = transformedBeanName(name);  
   Object beanInstance = getSingleton(beanName, false);  
   if (beanInstance != null) {  
      return (beanInstance instanceof FactoryBean);  
   }  
   // No singleton instance found -> check bean definition.  
  if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {  
      // No bean definition found in this factory -> delegate to parent.  
  return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);  
   }  
   return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));  
}

大致邏輯如下:

  1. transformedBeanName 的作用是不管傳入的引數是 &××× 還是 ×××,都返回 ×××。這是因為Spring標記 FactoryBean 時使用 &××× 作為 FactoryBeanbeanName
  2. getSingleton 方法從單例池中獲取 Bean 範例,如果該範例是 FactoryBean,則直接返回該範例。
  3. 如果 BeanFactory 中的 Bean 定義 Map 中不包含該 beanNameBean 定義,並且當前 BeanFactory 的父 BeanFactory 實現了 ConfigurableBeanFactory 介面,那麼就需要檢視當前父 BeanFactory 中是否有該範例,並且判斷該範例是否為 FactoryBean。舉個例子來說:
// 建立一個父Spring容器  
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();  
parent.register(AppConfig.class);  
parent.refresh();  
// 建立一個Spring容器  
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();  
applicationContext.setParent(parent);  
applicationContext.register(AppConfig1.class);  
applicationContext.refresh();  
UserService bean = applicationContext.getBean(UserService.class);  
bean.test();
  1. 如果並沒有範例化出來的bean,那麼對bean定義進行判斷。
protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {  
   Boolean result = mbd.isFactoryBean;  
   if (result == null) {  
      // 根據BeanDefinition推測Bean型別(獲取BeanDefinition的beanClass屬性)  
  Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);  
      // 判斷是不是實現了FactoryBean介面  
  result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));  
      mbd.isFactoryBean = result;  
   }  
   return result;  
}

註釋也基本寫好了,基本上就是根據BeanDefinition推測Bean型別(獲取BeanDefinition的beanClass屬性),再根據bean型別判斷是不是實現了FactoryBean介面,然後返回判斷結果。

SmartFactoryBean

getBean 方法中,我們可以獲取 FactoryBean 的範例並返回。接下來的步驟是判斷當前的 FactoryBean 是否實現了 SmartFactoryBean 介面。需要注意的是,SmartFactoryBeanFactoryBean 介面的一個子介面。雖然我們在實現 FactoryBean 介面時不必實現 SmartFactoryBean 介面,但是如果實現了 SmartFactoryBean 介面,那麼在建立 FactoryBean 時就會呼叫 getObject 方法返回範例。正常情況下,只有當容器啟動完成後才會呼叫 getObject 方法。如果我們想在初始化時就呼叫,可以這樣實現:

@Component  
public class UserFactory implements SmartFactoryBean {  
 
   @Override  
  public Object getObject() throws Exception {  
      return new User();  
   }  
  
   @Override  
  public Class<?> getObjectType() {  
      return User.class;  
   }  
  
   @Override  
  public boolean isEagerInit() {  
      return true;  
   }  
}

結語

FactoryBean 和 BeanFactory 是兩個不同的概念。前者是一個介面,我們可以在實現該介面時通過呼叫 getObject 方法來返回範例,同時 FactoryBean 本身也是一個範例。後者是 Spring 容器的工廠,通過其中的 bean 定義 Map 一個一個地範例化我們通過註解等方式注入進去的 bean 工廠。在判斷 FactoryBean 時,如果當前 BeanFactory 中沒有對應的 bean 定義,那麼就會去父容器中尋找相應的 bean 定義並進行判斷。如果我們的類實現了 SmartFactoryBean 介面,那麼它將會在 Spring 容器啟動時就會呼叫 getObject 方法建立範例。接下來,我們將分幾個小節來講解 getBean 方法是如何範例化 bean 的,因為篇幅過長會影響讀者的注意力和學習效果。

公眾號