歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之 bean 的實體化策略:InstantiationStrategy

在開始分析 InstantiationStrategy 之前,我們先來簡單回顧下 bean 的實體化過程:

  1. bean 的建立,主要是 AbstractAutowireCapableBeanFactory.doCreateBean() ,在這個方法中有 bean 的實體化、屬性註入和初始化過程,對於 bean 的實體化過程這是根據 bean 的型別來判斷的,如果是單例樣式,則直接從 factoryBeanInstanceCache 快取中獲取,否則呼叫 createBeanInstance() 建立。
  2. 在 createBeanInstance() 中,如果 Supplier 不為空,則呼叫 obtainFromSupplier() 實體化 bean。如果 factory 不為空,則呼叫 instantiateUsingFactoryMethod() 實體化 bean ,如果都不是則呼叫 instantiateBean() 實體化bean 。但是無論是 instantiateUsingFactoryMethod() 還是 instantiateBean() 最後都一定會呼叫到 InstantiationStrategy 介面的 instantiate()

InstantiationStrategy

InstantiationStrategy 介面定義了 Spring Bean 實體化的策略,根據建立物件情況的不同,提供了三種策略:無參構造方法、有參構造方法、工廠方法。如下:

  1. public interface InstantiationStrategy {
  2. /**
  3.  * 預設構造方法
  4.  */
  5. Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
  6.   throws BeansException;
  7. /**
  8.  * 指定構造方法
  9.  */
  10. Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
  11.   Constructor> ctor, @Nullable Object... args) throws BeansException;
  12. /**
  13.  * 工廠方法
  14.  */
  15. Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
  16.   @Nullable Object factoryBean, Method factoryMethod, @Nullable Object... args)
  17.   throws BeansException;
  18. }

SimpleInstantiationStrategy

InstantiationStrategy 介面有兩個實現類:SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy。SimpleInstantiationStrategy 對以上三個方法都做了簡單的實現。

如果是工廠方法實體化,則直接使用反射建立物件,如下:

  1. public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
  2.   @Nullable Object factoryBean, final Method factoryMethod, @Nullable Object... args) {
  3.  try {
  4.   if (System.getSecurityManager() != null) {
  5.    AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  6.     ReflectionUtils.makeAccessible(factoryMethod);
  7.     return null;
  8.    });
  9.   }
  10.   else {
  11.    ReflectionUtils.makeAccessible(factoryMethod);
  12.   }
  13.   Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
  14.   try {
  15.    currentlyInvokedFactoryMethod.set(factoryMethod);
  16.    Object result = factoryMethod.invoke(factoryBean, args);
  17.    if (result == null) {
  18.     result = new NullBean();
  19.    }
  20.    return result;
  21.   }
  22.   finally {
  23.    if (priorInvokedFactoryMethod != null) {
  24.     currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
  25.    }
  26.    else {
  27.     currentlyInvokedFactoryMethod.remove();
  28.    }
  29.   }
  30.  }
  31.  // 省略 catch
  32. }

如果是構造方法實體化,則是先判斷是否有 MethodOverrides,如果沒有則是直接使用反射,如果有則就需要 CGLIB 實體化物件。如下:

  1. public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
  2.  // Don't override the class with CGLIB if no overrides.
  3.  if (!bd.hasMethodOverrides()) {
  4.   Constructor> constructorToUse;
  5.   synchronized (bd.constructorArgumentLock) {
  6.    constructorToUse = (Constructor>) bd.resolvedConstructorOrFactoryMethod;
  7.    if (constructorToUse == null) {
  8.     final Class> clazz = bd.getBeanClass();
  9.     if (clazz.isInterface()) {
  10.      throw new BeanInstantiationException(clazz, "Specified class is an interface");
  11.     }
  12.     try {
  13.      if (System.getSecurityManager() != null) {
  14.       constructorToUse = AccessController.doPrivileged(
  15.         (PrivilegedExceptionAction<Constructor>>) clazz::getDeclaredConstructor);
  16.      }
  17.      else {
  18.       constructorToUse =    clazz.getDeclaredConstructor();
  19.      }
  20.      bd.resolvedConstructorOrFactoryMethod = constructorToUse;
  21.     }
  22.     catch (Throwable ex) {
  23.      throw new BeanInstantiationException(clazz, "No default constructor found", ex);
  24.     }
  25.    }
  26.   }
  27.   return BeanUtils.instantiateClass(constructorToUse);
  28.  }
  29.  else {
  30.   // Must generate CGLIB subclass.
  31.   return instantiateWithMethodInjection(bd, beanName, owner);
  32.  }
  33. }
  34. public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
  35.   final Constructor> ctor, @Nullable Object... args) {
  36.  if (!bd.hasMethodOverrides()) {
  37.   if (System.getSecurityManager() != null) {
  38.    // use own privileged to change accessibility (when security is on)
  39.    AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  40.     ReflectionUtils.makeAccessible(ctor);
  41.     return null;
  42.    });
  43.   }
  44.   return (args != null ? BeanUtils.instantiateClass(ctor, args) : BeanUtils.instantiateClass(ctor));
  45.  }
  46.  else {
  47.   return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);
  48.  }
  49. }

SimpleInstantiationStrategy 對 instantiateWithMethodInjection() 的實現任務交給了子類 CglibSubclassingInstantiationStrategy。

MethodOverrides

對於 MethodOverrides,如果讀者是跟著小編文章一路跟過來的話一定不會陌生,在 BeanDefinitionParserDelegate 類解析  的時候是否還記得這兩個方法: parseLookupOverrideSubElements() 和 parseReplacedMethodSubElements() 這兩個方法分別用於解析 lookup-method 和 replaced-method。 parseLookupOverrideSubElements() 原始碼如下:

更多關於 lookup-method 和 replaced-method 請看:【死磕 Spring】—– IOC 之解析 bean 標簽:meta、lookup-method、replace-method

CGLIB 實體化策略

類 CglibSubclassingInstantiationStrategy 為 Spring 實體化 bean 的預設實體化策略,其主要功能還是對父類功能進行補充:其父類將 CGLIB 的實體化策略委託其實現。

  1. --- SimpleInstantiationStrategy
  2. protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
  3.  throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");
  4. }
  5. --- CglibSubclassingInstantiationStrategy
  6. @Override
  7. protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
  8.  return instantiateWithMethodInjection(bd, beanName, owner, null);
  9. }

CglibSubclassingInstantiationStrategy 實體化 bean 策略是透過其內部類 CglibSubclassCreator 來實現的。

  1. protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
  2.   @Nullable Constructor> ctor, @Nullable Object... args) {
  3.  return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
  4. }

建立 CglibSubclassCreator 實體然後呼叫其 instantiate(),該方法用於動態建立子類實體,同時實現所需要的 lookups(lookup-method、replace-method)。

  1.  public Object instantiate(@Nullable Constructor> ctor, @Nullable Object... args) {
  2.   Class> subclass = createEnhancedSubclass(this.beanDefinition);
  3.   Object instance;
  4.   if (ctor == null) {
  5.    instance = BeanUtils.instantiateClass(subclass);
  6.   }
  7.   else {
  8.    try {
  9.     Constructor> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
  10.     instance = enhancedSubclassConstructor.newInstance(args);
  11.    }
  12.    catch (Exception ex) {
  13.     throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
  14.       "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
  15.    }
  16.   }
  17.    //這個地方解決一個bug,bug提交報告https://jira.spring.io/browse/SPR-10785
  18.    // SPR-10785: set callbacks directly on the instance instead of in the
  19.    // enhanced class (via the Enhancer) in order to avoid memory leaks.
  20.   Factory factory = (Factory) instance;
  21.   factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
  22.     new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
  23.     new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
  24.   return instance;
  25.  }

呼叫 createEnhancedSubclass() 為提供的 BeanDefinition 建立 bean 類的增強子類。

  1. private Class> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
  2. // cglib裡面的用法,對原始class進行增強,並設定callback
  3. Enhancer enhancer = new Enhancer();
  4. enhancer.setSuperclass(beanDefinition.getBeanClass());
  5. enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
  6. if (this.owner instanceof ConfigurableBeanFactory) {
  7.  ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
  8.  enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
  9. }
  10. // 過濾,自定義邏輯來指定呼叫的callback下標
  11. enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
  12. enhancer.setCallbackTypes(CALLBACK_TYPES);
  13. return enhancer.createClass();
  14. }

獲取子類增強 class 後,如果 Constructor 實體 ctr 為空,則呼叫預設建構式( BeanUtils.instantiateClass())來實體化類,否則則根據建構式型別獲取具體的建構式,呼叫 newInstance() 實體化類。在 createEnhancedSubclass() 我們註意兩行程式碼:

  1. enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
  2. enhancer.setCallbackTypes(CALLBACK_TYPES);

透過 MethodOverrideCallbackFilter 來定義呼叫 callback 型別,MethodOverrideCallbackFilter 是用來定義 CGLIB 回呼過濾方法的攔截器行為,它繼承 CglibIdentitySupport 實現 CallbackFilter 介面, CallbackFilter 是 CGLIB 的一個回呼過濾器,CglibIdentitySupport 則為 CGLIB 提供 hashCode() 和 equals() 方法,以確保 CGLIB 不會為每個 bean 生成不同的類。MethodOverrideCallbackFilter 實現 CallbackFilter accept()

  1.  public int accept(Method method) {
  2.   MethodOverride methodOverride = getBeanDefinition().getMethodOverrides().getOverride(method);
  3.   if (logger.isTraceEnabled()) {
  4.    logger.trace("Override for '" + method.getName() + "' is [" + methodOverride + "]");
  5.   }
  6.   if (methodOverride == null) {
  7.    return PASSTHROUGH;
  8.   }
  9.   else if (methodOverride instanceof LookupOverride) {
  10.    return LOOKUP_OVERRIDE;
  11.   }
  12.   else if (methodOverride instanceof ReplaceOverride) {
  13.    return METHOD_REPLACER;
  14.   }
  15.   throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
  16.     methodOverride.getClass().getName());
  17.  }

根據 BeanDefinition 中定義的 MethodOverride 不同,傳回不同的值, 這裡傳回的 PASSTHROUGH 、LOOKUPOVERRIDE、METHODREPLACER 都是 Callbak 陣列的下標,這裡對應的陣列為 CALLBACK_TYPES 陣列,如下:

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

這裡又定義了兩個熟悉的攔截器 :LookupOverrideMethodInterceptor 和 ReplaceOverrideMethodInterceptor,兩個攔截器分別對應兩個不同的 callback 業務:

LookupOverrideMethodInterceptor

  1. private static class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
  2.  private final BeanFactory owner;
  3.  public LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
  4.   super(beanDefinition);
  5.   this.owner = owner;
  6.  }
  7.  @Override
  8.  public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
  9.   // Cast is safe, as CallbackFilter filters are used selectively.
  10.   LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
  11.   Assert.state(lo != null, "LookupOverride not found");
  12.   Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
  13.   if (StringUtils.hasText(lo.getBeanName())) {
  14.    return (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
  15.      this.owner.getBean(lo.getBeanName()));
  16.   }
  17.   else {
  18.    return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
  19.      this.owner.getBean(method.getReturnType()));
  20.   }
  21.  }
  22. }

ReplaceOverrideMethodInterceptor

  1. private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
  2.  private final BeanFactory owner;
  3.  public ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
  4.   super(beanDefinition);
  5.   this.owner = owner;
  6.  }
  7.  @Override
  8.  public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
  9.   ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
  10.   Assert.state(ro != null, "ReplaceOverride not found");
  11.   // TODO could cache if a singleton for minor performance optimization
  12.   MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
  13.   return mr.reimplement(obj, method, args);
  14.  }
  15. }

透過這兩個攔截器,再加上這篇部落格:【死磕 Spring】—– IOC 之解析 bean 標簽:meta、lookup-method、replace-method,是不是一道絕佳的美食。

贊(0)

分享創造快樂