Spring 源碼(11)Spring Bean 的創建過程(2)

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,一個ObjectFactory的lambda表達式,表達式中有個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-method和replace-method 標籤的時候,就會進行方法的覆蓋。方法覆蓋有什麼用處呢?

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

做個實驗試試:

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

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

	public abstract MyCar park();

}

定義我的車

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

定義實現類:

寶馬車:

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

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

}

Benz車:

/**
 * @author <a href="//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="//www.springframework.org/schema/beans"
	   xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="//www.springframework.org/schema/context"
	   xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/context //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="//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類放入到BeanDefinition 的 MethodOverrides 屬性中,在進行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()));
  }
}

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