首頁 > 軟體

mybatis原始碼解讀之executor包懶載入功能 

2022-02-15 19:00:03

ProxyFactory是建立代理類的工廠介面,其中的setProperties方法用來對工廠進行屬性設定,但是mybatis內建的兩個實現類都沒有實現該介面,所以不支援屬性設定。createProxy方法用來建立一個代理物件

public interface ProxyFactory {

  // 設定工廠屬性
  default void setProperties(Properties properties) {
  }

  // 建立代理物件
  Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

}

ProxyFactory介面有2個實現類,CglibProxyFactoryJavassistProxyFactor類。這兩個實現類整體結構高度一致,內部類、方法設定都一樣,只是實現原理不同。CglibProxyFactory基於cglib實現,JavassistProxyFactor基於javassist實現。

接下來以CglibProxyFactory類為原始碼進行分析:

CglibProxyFactory類中提供了兩個建立代理物件的方法。其中createProxy方法重寫了一個普通的代理物件,createDeserializationProxy方法用來建立一個反序列化的代理物件。

public class CglibProxyFactory implements ProxyFactory {

  private static final String FINALIZE_METHOD = "finalize";
  private static final String WRITE_REPLACE_METHOD = "writeReplace";

  public CglibProxyFactory() {
    try {
      Resources.classForName("net.sf.cglib.proxy.Enhancer");
    } catch (Throwable e) {
      throw new IllegalStateException("Cannot enable lazy loading because CGLIB is not available. Add CGLIB to your classpath.", e);
    }
  }

  // 建立一個代理
  @Override
  public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
  }

  // 建立一個反序列化的代理
  public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
  }
  private static class EnhancedResultObjectProxyImpl implements MethodInterceptor {

    // 被代理類
    private final Class<?> type;
    // 要懶載入的屬性Map
    private final ResultLoaderMap lazyLoader;
    // 是否是激進懶載入
    private final boolean aggressive;
    // 能夠觸發懶載入的方法名「equals」, 「clone」, 「hashCode」, 「toString」。這四個方法名在Configuration中被初始化。
    private final Set<String> lazyLoadTriggerMethods;
    // 物件工廠
    private final ObjectFactory objectFactory;
    // 被代理類建構函式的引數型別列表
    private final List<Class<?>> constructorArgTypes;
    // 被代理類建構函式的參數列
    private final List<Object> constructorArgs;

    private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
      this.type = type;
      this.lazyLoader = lazyLoader;
      this.aggressive = configuration.isAggressiveLazyLoading();
      this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
      this.objectFactory = objectFactory;
      this.constructorArgTypes = constructorArgTypes;
      this.constructorArgs = constructorArgs;
    }

    public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
      final Class<?> type = target.getClass();
      EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
      Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
      PropertyCopier.copyBeanProperties(type, target, enhanced);
      return enhanced;
    }

    /**
     * 代理類的攔截方法
     * @param enhanced 代理物件本身
     * @param method 被呼叫的方法
     * @param args 每呼叫的方法的引數
     * @param methodProxy 用來呼叫父類別的代理
     * @return 方法返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
      // 取出被代理類中此次被呼叫的方法的名稱
      final String methodName = method.getName();
      try {
        synchronized (lazyLoader) { // 防止屬性的並行載入
          if (WRITE_REPLACE_METHOD.equals(methodName)) { // 被呼叫的是writeReplace方法
            // 建立一個原始物件
            Object original;
            if (constructorArgTypes.isEmpty()) {
              original = objectFactory.create(type);
            } else {
              original = objectFactory.create(type, constructorArgTypes, constructorArgs);
            }
            // 將被代理物件的屬性拷貝進入新建立的物件
            PropertyCopier.copyBeanProperties(type, enhanced, original);
            if (lazyLoader.size() > 0) { // 存在懶載入屬性
              // 則此時返回的資訊要更多,不僅僅是原物件,還有相關的懶載入的設定等資訊。因此使用CglibSerialStateHolder進行一次封裝
              return new CglibSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
            } else {
              // 沒有未懶載入的屬性了,那直接返回原物件進行序列化
              return original;
            }
          } else {
            if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) { // 存在懶載入屬性且被呼叫的不是finalize方法
              if (aggressive || lazyLoadTriggerMethods.contains(methodName)) { // 設定了激進懶載入或者被呼叫的方法是能夠觸發全域性懶載入的方法
                // 完成所有屬性的懶載入
                lazyLoader.loadAll();
              } else if (PropertyNamer.isSetter(methodName)) { // 呼叫了屬性寫方法
                // 則先清除該屬性的懶載入設定。該屬性不需要被懶載入了
                final String property = PropertyNamer.methodToProperty(methodName);
                lazyLoader.remove(property);
              } else if (PropertyNamer.isGetter(methodName)) { // 呼叫了屬性讀方法
                final String property = PropertyNamer.methodToProperty(methodName);
                // 如果該屬性是尚未載入的懶載入屬性,則進行懶載入
                if (lazyLoader.hasLoader(property)) {
                  lazyLoader.load(property);
                }
              }
            }
          }
        }
        // 觸發被代理類的相應方法。能夠進行到這裡的是除去writeReplace方法外的方法,例如讀寫方法、toString方法等
        return methodProxy.invokeSuper(enhanced, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
  }
  }

代理類最核心的方法是intercept方法,當被代理物件的其他方法被呼叫時,intercept方法的處理方式是:

如果設定了激進懶載入或者被呼叫的是觸發全域性載入的方法,則直接載入所有未載入的屬性。

如果被呼叫的是屬性寫方法,則將該方法從懶載入列表中刪除,因為此時資料庫中的資料已經不是最新的,沒有必要再去載入,然後進行屬性的寫入操作。

如果被呼叫的是讀方法,則該屬性尚未被懶載入的情況下,則載入該屬性,如果該屬性已經被懶載入過,則直接讀取該屬性。

ResultLoaderMap類:

被代理物件可能會有多個屬性可以被懶載入,這些尚未完成載入的屬性是在ResultLoaderMap類的範例中儲存的。ResultLoaderMap類主要就是一個map類,該類key為屬性名的大寫,value為LoadPair物件。LoadPair類是ResultLoaderMap類的內部類,它能實現對應屬性的懶載入功能。

public static class LoadPair implements Serializable {
    private static final long serialVersionUID = 20130412;
  
    // 用來根據反射得到資料庫連線的方法名
    private static final String FACTORY_METHOD = "getConfiguration";
    
    // 判斷是否經過了序列化的標誌位,因為該屬性被設定了transient,經過一次序列化和反序列化後會變為null
    private final transient Object serializationCheck = new Object();
   
    // 輸出結果物件的封裝
    private transient MetaObject metaResultObject;
   
    // 用以載入未載入屬性的載入器
    private transient ResultLoader resultLoader;
  
    // 紀錄檔記錄器
    private transient Log log;
    
    // 用來獲取資料庫連線的工廠
    private Class<?> configurationFactory;
    
    // 未載入的屬性的屬性名
    private String property;
   
    // 能夠載入未載入屬性的SQL的編號
    private String mappedStatement;
  
    // 能夠載入未載入屬性的SQL的引數
    private Serializable mappedParameter;

    private LoadPair(final String property, MetaObject metaResultObject, ResultLoader resultLoader) {
      this.property = property;
      this.metaResultObject = metaResultObject;
      this.resultLoader = resultLoader;

      if (metaResultObject != null && metaResultObject.getOriginalObject() instanceof Serializable) {
        final Object mappedStatementParameter = resultLoader.parameterObject;

        if (mappedStatementParameter instanceof Serializable) {
          this.mappedStatement = resultLoader.mappedStatement.getId();
          this.mappedParameter = (Serializable) mappedStatementParameter;

          this.configurationFactory = resultLoader.configuration.getConfigurationFactory();
        } else {
          Log log = this.getLogger();
          if (log.isDebugEnabled()) {
            log.debug("Property [" + this.property + "] of ["
                    + metaResultObject.getOriginalObject().getClass() + "] cannot be loaded "
                    + "after deserialization. Make sure it's loaded before serializing "
                    + "forenamed object.");
          }
        }
      }
    }

    public void load() throws SQLException {
      if (this.metaResultObject == null) {
        throw new IllegalArgumentException("metaResultObject is null");
      }
      if (this.resultLoader == null) {
        throw new IllegalArgumentException("resultLoader is null");
      }

      this.load(null);
    }

    /**
     * 進行載入操作
     * @param userObject 需要被懶載入的物件(只有當this.metaResultObject == null || this.resultLoader == null才生效,否則會採用屬性metaResultObject對應的物件)
     * @throws SQLException
     */
    public void load(final Object userObject) throws SQLException {
      if (this.metaResultObject == null || this.resultLoader == null) { // 輸出結果物件的封裝不存在或者輸出結果載入器不存在
        // 判斷用以載入屬性的對應的SQL語句存在
        if (this.mappedParameter == null) {
          throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
                  + "required parameter of mapped statement ["
                  + this.mappedStatement + "] is not serializable.");
        }

        final Configuration config = this.getConfiguration();
        // 取出用來載入結果的SQL語句
        final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
        if (ms == null) {
          throw new ExecutorException("Cannot lazy load property [" + this.property
                  + "] of deserialized object [" + userObject.getClass()
                  + "] because configuration does not contain statement ["
                  + this.mappedStatement + "]");
        }

        // 建立結果物件的包裝
        this.metaResultObject = config.newMetaObject(userObject);
        // 建立結果載入器
        this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
                metaResultObject.getSetterType(this.property), null, null);
      }

      // 只要經歷過持久化,則可能在別的執行緒中了。為這次惰性載入建立的新執行緒ResultLoader
      if (this.serializationCheck == null) {
        // 取出原來的ResultLoader中的必要資訊,然後建立一個新的
        // 這是因為load函數可能在不同的時間多次執行(第一次載入屬性A,又過了好久載入屬性B)。
        // 而該物件的各種屬性是跟隨物件的,載入屬性B時還保留著載入屬性A時的狀態,即ResultLoader是載入屬性A時設定的
        // 則此時ResultLoader中的Executor在ResultLoader中被替換成了一個能執行的Executor,而不是ClosedExecutor
        // 能執行的Executor的狀態可能不是close,這將導致它被複用,從而引發多執行緒問題
        // 是不是被兩次執行的一個關鍵點就是有沒有經過序列化,因為執行完後會被序列化並持久化
        final ResultLoader old = this.resultLoader;
        this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
                old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
      }

      this.metaResultObject.setValue(property, this.resultLoader.loadResult());
    }

    private Configuration getConfiguration() {
      if (this.configurationFactory == null) {
        throw new ExecutorException("Cannot get Configuration as configuration factory was not set.");
      }

      Object configurationObject;
      try {
        final Method factoryMethod = this.configurationFactory.getDeclaredMethod(FACTORY_METHOD);
        if (!Modifier.isStatic(factoryMethod.getModifiers())) {
          throw new ExecutorException("Cannot get Configuration as factory method ["
                  + this.configurationFactory + "]#["
                  + FACTORY_METHOD + "] is not static.");
        }

        if (!factoryMethod.isAccessible()) {
          configurationObject = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
            try {
              factoryMethod.setAccessible(true);
              return factoryMethod.invoke(null);
            } finally {
              factoryMethod.setAccessible(false);
            }
          });
        } else {
          configurationObject = factoryMethod.invoke(null);
        }
      } catch (final ExecutorException ex) {
        throw ex;
      } catch (final NoSuchMethodException ex) {
        throw new ExecutorException("Cannot get Configuration as factory class ["
                + this.configurationFactory + "] is missing factory method of name ["
                + FACTORY_METHOD + "].", ex);
      } catch (final PrivilegedActionException ex) {
        throw new ExecutorException("Cannot get Configuration as factory method ["
                + this.configurationFactory + "]#["
                + FACTORY_METHOD + "] threw an exception.", ex.getCause());
      } catch (final Exception ex) {
        throw new ExecutorException("Cannot get Configuration as factory method ["
                + this.configurationFactory + "]#["
                + FACTORY_METHOD + "] threw an exception.", ex);
      }

      if (!(configurationObject instanceof Configuration)) {
        throw new ExecutorException("Cannot get Configuration as factory method ["
                + this.configurationFactory + "]#["
                + FACTORY_METHOD + "] didn't return [" + Configuration.class + "] but ["
                + (configurationObject == null ? "null" : configurationObject.getClass()) + "].");
      }

      return Configuration.class.cast(configurationObject);
    }

    private Log getLogger() {
      if (this.log == null) {
        this.log = LogFactory.getLog(this.getClass());
      }
      return this.log;
    }
  }

到此這篇關於mybatis原始碼解讀之executor包懶載入功能 的文章就介紹到這了,更多相關executor包懶載入功能 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com