Spring 原始碼(10)Spring Bean 的建立過程(1)

玲丶蹊發表於2022-05-09

Spring Bean的建立剛開始進行了一些準備工作,比如轉換服務的初始化,佔位符解析器的初始化,BeanDefinition後設資料的凍結等操作,都是為了在建立Bean的過程中保證Bean的正確的建立,接下來開始進行對Bean的建立進行解析。

Bean 的建立步驟

Spring原始碼中對Bean的建立遵循一個步驟就是:getBean --> doGetBean --> createBean --> doCreateBean ,常規的Bean的建立過程都是按照這個步驟執行,然後反射例項化,屬性填充,初始化,放到一級快取中。那麼非常規的有可能就不遵循這個步驟,比如FactoryBeanInstantiationAwareBeanPostProcessor 等。

上原始碼:

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...
  // 遍歷所有的beanName
  for (String beanName : beanNames) {
    // 獲取RootBeanDefinition 從快取中,第一個放入快取是在 AbstractApplicationContext#invokeBeanFactoryPostProcessors 中的getBeanNamesForType方法中
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    // 如果不是抽象的,是單例的,是非懶載入的,則進行bean的建立,否則直接跳過
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
      // 是否是FactoryBean
      if (isFactoryBean(beanName)) {
        // 獲取bean例項
        Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
        // 判斷獲取的Bean是否是FactoryBean
        if (bean instanceof FactoryBean) {
          FactoryBean<?> factory = (FactoryBean<?>) bean;
          // 是否是飢餓初始化,預設是false
          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());
          }
          // 如果是飢餓初始化,則進行bean的建立
          if (isEagerInit) {
            getBean(beanName);
          }
        }
      }
      else {
        // 獲取bean
        getBean(beanName);
      }
    }
  }

  // Trigger post-initialization callback for all applicable beans...
  // 觸發 所有Bean初始化後的回撥
  for (String beanName : beanNames) {
    Object singletonInstance = getSingleton(beanName);
    // 獲取單例物件,如果是SmartInitializingSingleton 則呼叫afterSingletonsInstantiated
    // 在監聽器中使用@EventListener註解標記的方法就是在這個方法中進行監聽器的新增的,會建立一個監聽器的介面卡
    // 呼叫類為 EventListenerMethodProcessor
    if (singletonInstance instanceof SmartInitializingSingleton) {
      SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
      // 許可權檢查
      if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
          smartSingleton.afterSingletonsInstantiated();
          return null;
        }, getAccessControlContext());
      }
      else {
        smartSingleton.afterSingletonsInstantiated();
      }
    }
  }
}

通過原始碼可以知道,Spring前期在進行XML進行loadBeanDefinitions載入或者BeanFactoryPostProcessor子類BeanDefinitionRegistryPostProcessor的實現類ConfigurationClassPostProcessor註解解析 出來的BeanDefinition放入兩個集合BeanDefinitionMapBeanDefinitionNames,這裡遍歷的是BeanDefinitionNames這個集合,存放的是beanName

首先是進行了BeanDefinition的合併處理,最終返回的全是RootBeanDefinition,進入原始碼可以看到這裡是從快取中獲取的,如果有則直接取出來,否則再去解析。

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
  // Quick check on the concurrent map first, with minimal locking.
  // 從快取中獲取
  RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
  if (mbd != null && !mbd.stale) {
    return mbd;
  }
  return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

那麼第一次進行呼叫時什麼地方呢?是在進行BeanFactoryPostProcessor 的執行和解析時呼叫的,在解析BeanFactoryPostProcessor時呼叫了 getBeanNamesForType方法,然後呼叫doGetBeanNamesForType時進行了BeanDefinitionNames集合的遍歷合併Bean

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
  List<String> result = new ArrayList<>();
  // Check all bean definitions.
  // 遍歷所有的BeanDefinitionNames集合
  for (String beanName : this.beanDefinitionNames) {
    // Only consider bean as eligible if the bean name is not defined as alias for some other bean.
    if (!isAlias(beanName)) {
      try {
        // 從本地快取中獲取合併的BeanDefinition
        RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        // Only check bean definition if it is complete.
        if (!mbd.isAbstract() && (allowEagerInit ||
                                  (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
                                  !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
          // 是否是FactoryBean
          boolean isFactoryBean = isFactoryBean(beanName, mbd);
          BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
          boolean matchFound = false;
          boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
          boolean isNonLazyDecorat
            // 省略程式碼....
        }
      }
    }
  }

所以在執行preInstantiateSingletons 預例項化單例時獲取的RootBeanDefinition基本是從快取中獲取的。

接著是判斷如果是單例的並且不是抽象的,不是懶載入的,那麼就進行Bean的建立,然後又判斷是否是FactoryBean,如果是那麼就進行下一步邏輯。

FactoryBean 是什麼?

FactoryBean是用來建立Bean物件的,他是一個介面,方法:

  • getObject 獲取bean物件
  • getObjectType 獲取bean的型別
  • isSingleton 是否是單例的,預設是true

在建立物件時,你可以直接在getObject方法中進行new,或者反射,或者是其他都可以,非常的靈活。接下來使用FactoryBean進行自定義的Bean的建立。

定義一個FactoryBean的實現類:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class MyFactoryBean implements FactoryBean<MyUser> {

	@Override
	public MyUser getObject() throws Exception {
      	// 直接new一個物件
		return new MyUser();
	}

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

	@Override
	public boolean isSingleton() {
		return true;
	}
}

定義MyUser

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class MyUser {
}

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
   <bean id="myFactoryBean" class="com.redwinter.selffactorybean.MyFactoryBean"/>
</beans>

測試類:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class FactoryBeanTest {

	@Test
	public void test(){
		MyClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("spring-factory.xml");

		Object myFactoryBean =  context.getBean("myFactoryBean");
		System.out.println(myFactoryBean);

		Object myFactoryBean2 =  context.getBean("&myFactoryBean");
		System.out.println(myFactoryBean2);

	}
}

輸出:

com.redwinter.test.selffactorybean.MyUser@2d554825
com.redwinter.test.selffactorybean.MyFactoryBean@68837a77

這裡可以看到FactoryBean 建立Bean的時候,xml註冊的是一個FactoryBean的實現,但是獲取出來又是具體的MyUser物件,這裡Spring使用了懶載入的機制,在SpringBean進行初始化時,實際上只將FactoryBean的實現類註冊到了Spring容器中,當我們需要使用的時候,才去判斷,如果是FactoryBean型別的,那麼就去呼叫getObject方法去建立物件。如果是第二次去獲取Bean,那麼是從快取中獲取的,如果是獲取&字首的Bean,那就直接返回。

protected Object getObjectForBeanInstance(
  Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

  // Don't let calling code try to dereference the factory if the bean isn't a factory.
  // 判斷是否是&字首標識
  if (BeanFactoryUtils.isFactoryDereference(name)) {
    if (beanInstance instanceof NullBean) {
      return beanInstance;
    }
    if (!(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
    }
    if (mbd != null) {
      mbd.isFactoryBean = true;
    }
    return beanInstance;
  }

  // Now we have the bean instance, which may be a normal bean or a FactoryBean.
  // If it's a FactoryBean, we use it to create a bean instance, unless the
  // caller actually wants a reference to the factory.
  if (!(beanInstance instanceof FactoryBean)) {
    return beanInstance;
  }

  Object object = null;
  if (mbd != null) {
    mbd.isFactoryBean = true;
  }
  else {
    // 從快取中獲取
    object = getCachedObjectForFactoryBean(beanName);
  }
  if (object == null) {
    // Return bean instance from factory.
    FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
    // Caches object obtained from FactoryBean if it is a singleton.
    if (mbd == null && containsBeanDefinition(beanName)) {
      mbd = getMergedLocalBeanDefinition(beanName);
    }
    // 判斷是否是合成的Bean,是否是應用程式本身設定的,比如某些aop 就是合成的Bean
    boolean synthetic = (mbd != null && mbd.isSynthetic());
    // 執行getObject方法獲取Bean
    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
  }
  return object;
}

BeanFactory 和FactoryBean 的區別?

根據前面的文章介紹,我們知道BeanFactory是一個Bean的建立工廠,比如AbstractApplicationContext就是BeanFactory的實現類,這個類就是用來建立Bean的,建立出來的Bean放在快取中。而FactoryBean就是Bean例項,是由BeanFactory建立的,並且FactoryBean也是用來建立Bean物件,使用getObject方法進行建立,也是會放在快取中供下次直接獲取,而且如果在使用時需要使用FactoryBean的例項時需要以&字首才能獲取到,比如getBean("&myFactoryBean"); 如果是獲取通過getObject方法建立的物件時,就不需要新增&字首,比如getBean("myFactoryBean"); 總結一下:

相同點:

  • 都是用來建立物件的
  • 都是建立出來之後放入快取中供下次直接使用

不同點:

  • BeanFactory是一個物件建立工廠,而FactoryBean是一個Bean例項
  • BeanFactory建立的物件一般來說都是使用反射呼叫建構函式建立的,而FactoryBean建立物件是呼叫getObject方法建立,並且建立方式不一定是通過反射,可以是直接new物件或者其他方式
  • FactoryBean 在獲取物件時,可以獲取到兩個物件,一個是存放在BeanFactory建立的快取中,通過&beanName獲取的FactoryBean的實現類物件,一個是呼叫getObject建立的,通過beanName獲取的具體物件。

Bean的建立過程非常複雜,下一篇繼續。

相關文章