1. 程式人生 > 程式設計 >Mybatis懶載入的實現

Mybatis懶載入的實現

因為通過javassist和cglib代理實現的,所以說到底最主要的就是JavasisstProxyFactory類中的invoke方法和裡面的load方法。

其實讀一讀,裡面的邏輯就是跟配置中定義的規則一樣的
因為github上的mybatis中文版中這部分註釋比較少,所以從網上尋找部落格,截取了程式碼註釋片段記錄下。
JavasisstProxyFactory

public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {

 
 /**
 * 介面實現
 * @param target 目標結果物件
 * @param lazyLoader 延遲載入物件
 * @param configuration 配置
 * @param objectFactory 物件工廠
 * @param constructorArgTypes 構造引數型別
 * @param constructorArgs 構造引數值
 * @return
 */
 @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);
 }

 //省略...
 
 /**
 * 代理物件實現,核心邏輯執行
 */
 private static class EnhancedResultObjectProxyImpl implements MethodHandler {
 
 /**
 * 建立代理物件
 * @param type
 * @param callback
 * @param constructorArgTypes
 * @param constructorArgs
 * @return
 */
 static Object crateProxy(Class<?> type,MethodHandler callback,List<Object> constructorArgs) {

 ProxyFactory enhancer = new ProxyFactory();
 enhancer.setSuperclass(type);

 try {
  //通過獲取物件方法,判斷是否存在該方法
  type.getDeclaredMethod(WRITE_REPLACE_METHOD);
  // ObjectOutputStream will call writeReplace of objects returned by writeReplace
  if (log.isDebugEnabled()) {
  log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ",make sure it returns this");
  }
 } catch (NoSuchMethodException e) {
  //沒找到該方法,實現介面
  enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class});
 } catch (SecurityException e) {
  // nothing to do here
 }

 Object enhanced;
 Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
 Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
 try {
  //建立新的代理物件
  enhanced = enhancer.create(typesArray,valuesArray);
 } catch (Exception e) {
  throw new ExecutorException("Error creating lazy proxy. Cause: " + e,e);
 }
 //設定代理執行器
 ((Proxy) enhanced).setHandler(callback);
 return enhanced;
 }
 
 
  /**
  * 代理物件執行
  * @param enhanced 原物件
  * @param method 原物件方法
  * @param methodProxy 代理方法
  * @param args 方法引數
  * @return
  * @throws Throwable
  */
 @Override
 public Object invoke(Object enhanced,Method method,Method methodProxy,Object[] args) throws Throwable {
  final String methodName = method.getName();
  try {
  synchronized (lazyLoader) {
   if (WRITE_REPLACE_METHOD.equals(methodName)) { 
   //忽略暫未找到具體作用
   Object original;
   if (constructorArgTypes.isEmpty()) {
    original = objectFactory.create(type);
   } else {
    original = objectFactory.create(type,constructorArgs);
   }
   PropertyCopier.copyBeanProperties(type,enhanced,original);
   if (lazyLoader.size() > 0) {
    return new JavassistSerialStateHolder(original,lazyLoader.getProperties(),constructorArgs);
   } else {
    return original;
   }
   } else {
    //延遲載入數量大於0
   if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
    //aggressive 一次載入性所有需要要延遲載入屬性或者包含觸發延遲載入方法
    if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
    log.debug("==> laze lod trigger method:" + methodName + ",proxy method:" + methodProxy.getName() + " class:" + enhanced.getClass());
    //一次全部載入
    lazyLoader.loadAll();
    } else if (PropertyNamer.isSetter(methodName)) {
    //判斷是否為set方法,set方法不需要延遲載入
    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);
     log.debug("load one :" + methodName);
    }
    }
   }
   }
  }
  return methodProxy.invoke(enhanced,args);
  } catch (Throwable t) {
  throw ExceptionUtil.unwrapThrowable(t);
  }
 }
 }

load方法

/**
  * 執行懶載入查詢,獲取資料並且set到userObject中返回
  * @param userObject
  * @throws SQLException
  */
 public void load(final Object userObject) throws SQLException {
  
  //合法性校驗
  if (this.metaResultObject == null || this.resultLoader == null) {
  if (this.mappedParameter == null) {
   throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
     + "required parameter of mapped statement ["
     + this.mappedStatement + "] is not serializable.");
  }
  
  //獲取mappedstatement並且校驗
  final Configuration config = this.getConfiguration();
  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 + "]");
  }
 
  //使用userObject構建metaobject,並且重新構建resultloader物件
  this.metaResultObject = config.newMetaObject(userObject);
  this.resultLoader = new ResultLoader(config,new ClosedExecutor(),ms,this.mappedParameter,metaResultObject.getSetterType(this.property),null,null);
  }
 
  /* We are using a new executor because we may be (and likely are) on a new thread
  * and executors aren't thread safe. (Is this sufficient?)
  *
  * A better approach would be making executors thread safe. */
  if (this.serializationCheck == null) {
  final ResultLoader old = this.resultLoader;
  this.resultLoader = new ResultLoader(old.configuration,old.mappedStatement,old.parameterObject,old.targetType,old.cacheKey,old.boundSql);
  }
 
  //獲取資料庫查詢結果並且set到結果物件返回
  this.metaResultObject.setValue(property,this.resultLoader.loadResult());
 }

參考地址:
https://www.cnblogs.com/qixidi/p/10251126.html
https://blog.csdn.net/mingtian625/article/details/47358003

到此這篇關於Mybatis懶載入的實現的文章就介紹到這了,更多相關Mybatis 懶載入內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!