Spring 原始碼(11)Spring Bean 的建立過程(2)

玲丶蹊發表於2022-05-10

Spring Bean 的建立過程介紹了FactoryBean 的建立方式,那麼接下來介紹不是FactoryBean的建立方式,在建立過程中,又會分為單例的Bean的建立,原型型別的Bean的建立等。一般來說在Spring中幾乎所有物件都是單例建立的,除非有其他業務需要設定為其他作用域的Bean,所以重點以建立單例Bean為例。

單例Bean的建立

在建立時會呼叫getBean,然後doGetBean,一般來說在Spring中只要是do開頭方法基本就是真正幹活的方法,所以我們看doGetBean方法的原始碼:

protected <T> T doGetBean(
  String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
  throws BeansException {
  // 解析成規範的Bean name ,因為可能是FactoryBean加了& 字首的Bean或者是有別名的Bean
  String beanName = transformedBeanName(name);
  Object bean;
  // Eagerly check singleton cache for manually registered singletons.
  // 獲取快取中的Bean
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }
  // 如果快取中沒有,那麼就會按照單例或者多例的方式建立
  else {
    // 省略程式碼....

    // Check if bean definition exists in this factory.
    // 檢查父類容器
    // 省略程式碼....

    if (!typeCheckOnly) {
      // 標記已經被建立
      markBeanAsCreated(beanName);
    }

    try {
      // 合併BeanDefinition
      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      checkMergedBeanDefinition(mbd, beanName, args);

      // Guarantee initialization of beans that the current bean depends on.
      // 判斷是否存在依賴的Bean的建立,比如dependsOn 依賴 A 這個Bean,那麼就需要先建立A這個bean
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
        for (String dep : dependsOn) {
          if (isDependent(beanName, dep)) {
            // 省略程式碼....
          }
          // 註冊依賴的Bean,放在集合中
          registerDependentBean(dep, beanName);
          try {
            // 建立Bean
            getBean(dep);
          }
          catch (NoSuchBeanDefinitionException ex) {
           // 省略程式碼....
          }
        }
      }

      // Create bean instance.
      if (mbd.isSingleton()) {
        // 如果是單例的,就去建立Bean
        sharedInstance = getSingleton(beanName, () -> {
          try {
            // 建立Bean
            return createBean(beanName, mbd, args);
          }
          catch (BeansException ex) {
            // 省略程式碼....
          }
        });
        // 獲取bean物件,會進行檢查獲取物件是否是FactoryBean
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }
      // 原型作用的建立方式
      else if (mbd.isPrototype()) {
       // 省略程式碼....
      }
      else {
        // 省略程式碼....
      }
    }
    catch (BeansException ex) {
      // 省略程式碼....
    }
  }
  // 省略程式碼....
  return (T) bean;
}

去掉不重要的程式碼,可以看到首先是從快取中獲取,如果沒有獲取到就進行一些列檢查,最終檢查是否單例的Bean,如果是,那麼就會呼叫getSingleton方法,傳入一個beanName,一個ObjectFactorylambda表示式,表示式中有個createBean方法,這個方法就是建立的Bean方法。

那什麼時候呼叫crateBean方法呢?

答案是執行lambda表示式的具體方法時執行,我們先看看這個ObjectFactory介面是啥?

ObjectFactory 物件工廠

直接看原始碼:

@FunctionalInterface
public interface ObjectFactory<T> {

	/**
	 * Return an instance (possibly shared or independent)
	 * of the object managed by this factory.
	 * @return the resulting instance
	 * @throws BeansException in case of creation errors
	 */
	T getObject() throws BeansException;

}

這個介面是一個函式式介面,可以用於lambda表示式直接使用,在呼叫getObject方法時就是真正執行lambda表示式中的方法。

具體看看getSingleton方法的原始碼:

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) {
      if (this.singletonsCurrentlyInDestruction) {
        // 省略程式碼....
      }
      // 省略程式碼....
      // 檢查 並新增正在建立的單例物件到集合中
      beforeSingletonCreation(beanName);
      // 設定為新的單例物件標識
      boolean newSingleton = false;
      // 設定異常集合,出現異常時將異常加入到集合中
      boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
      if (recordSuppressedExceptions) {
        this.suppressedExceptions = new LinkedHashSet<>();
      }
      try {
        // 執行具體的方法,呼叫crateBean方法
        singletonObject = singletonFactory.getObject();
        // 標識為新的單例物件
        newSingleton = true;
      }
      catch (IllegalStateException ex) {
        // 省略程式碼....
      }
      catch (BeanCreationException ex) {
        // 省略程式碼....
      }
      finally {
        if (recordSuppressedExceptions) {
          this.suppressedExceptions = null;
        }
        // 檢查 並移除正在建立的單例物件
        afterSingletonCreation(beanName);
      }
      if (newSingleton) {
        // 加入到快取中
        addSingleton(beanName, singletonObject);
      }
    }
    return singletonObject;
  }

這裡執行完之後就會執行到lambda表示式中的createBean方法:

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

   // 省略程式碼....
   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.
   // 解析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.
   try {
      // 方法覆蓋準備 lookup-method replace-method
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      // 省略程式碼....
   }

   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      // 解析提前例項化,使用InstantiationAwareBeanPostProcessor實現
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
      // 省略程式碼....
   }

   try {
      // 例項化 + 初始化 Bean
      // 真正的建立Bean
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      // 省略程式碼....
      return beanInstance;
   }
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      // 省略程式碼....
   }
   catch (Throwable ex) {
      // 省略程式碼....
   }
}

首先是進行了Bean的型別的解析,主要是用於後面的反射建立物件時使用,並設定到RootBeanDefinition中,然後進行方法覆蓋操作。

Spring方法覆蓋實戰

方法覆蓋就是使用了lookup-methodreplace-method 標籤的時候,就會進行方法的覆蓋。方法覆蓋有什麼用處呢?

一般來說方法覆蓋就是解決單例物件引用多例物件的時候使用方法覆蓋。

做個實驗試試:

定義一個房子類,用於停車

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

	public abstract MyCar park();

}

定義我的車

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public interface MyCar {
	/**
	 * 買一輛車
	 * @return 車
	 */
	MyCar buy();
}

定義實現類:

寶馬車:

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

	@Override
	public MyCar buy() {
		return this;
	}

}

賓士車:

/**
 * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
 * @since 1.0
 **/
public class Ben implements MyCar{
	@Override
	public MyCar buy() {
		return this;
	}

}

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="myHouse1" class="com.redwinter.test.methodoverride.lookupmethod.MyHouse" >
		<lookup-method name="park" bean="bmw"/>
	</bean>
	<bean id="myHouse2" class="com.redwinter.test.methodoverride.lookupmethod.MyHouse" >
		<lookup-method name="park" bean="ben"/>
	</bean>
   <!-- 設定為原型-->
	<bean id="bmw" class="com.redwinter.test.methodoverride.lookupmethod.BMW" scope="prototype"/>
	<bean id="ben" class="com.redwinter.test.methodoverride.lookupmethod.Ben"/>
</beans>

測試類:

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

	/**
	 * lookup-method 用來解決單例物件多例物件的
	 */
	@Test
	public void lookupTest(){
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:method-override.xml");
		MyHouse myHouse = (MyHouse) context.getBean("myHouse1");
		MyHouse myHouse1 = (MyHouse) context.getBean("myHouse1");
		System.out.println(myHouse.park());
		System.out.println(myHouse1.park());
	}
}

輸出:

com.redwinter.test.methodoverride.lookupmethod.BMW@4a765
com.redwinter.test.methodoverride.lookupmethod.BMW@3e6358

這裡Myhouse是一個單例的物件,myHouse1 呼叫的方法每次呼叫都是不同的物件。

Spring是如何實現方法覆蓋的?

原始碼過於繁瑣和複雜,這裡直接看執行流程:

Spring在載入BeanDefinition的時候,執行 parseLookupOverrideSubElements 這個方法的時候只要設定了lookup-method標籤就會建立一個LookupOverride類放入到BeanDefinitionMethodOverrides 屬性中,在進行Bean的建立的時候,就會判斷這個屬性值是否有值,如果有那麼就會在物件例項化時獲取一個例項化策略,然後執行例項化,就、就會呼叫SimpleInstantiationStrategy#instantiate 方法,然後使用CGLIB進行例項化,建立出一個Enhancer 增強類,並且設定一個回撥型別為:

private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
				{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};

最終在執行方法的時候就會呼叫到回撥類LookupOverrideMethodInterceptor 攔截器上,然後執行Bean的建立:

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
  // Cast is safe, as CallbackFilter filters are used selectively.
  LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
  Assert.state(lo != null, "LookupOverride not found");
  Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
  if (StringUtils.hasText(lo.getBeanName())) {
    // 建立Bean
    Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
                   this.owner.getBean(lo.getBeanName()));
    // Detect package-protected NullBean instance through equals(null) check
    return (bean.equals(null) ? null : bean);
  }
  else {
    return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
            this.owner.getBean(method.getReturnType()));
  }
}

這篇文章就介紹到這裡,下一篇繼續。

相關文章