Mybatis文件加載第九章

Mybatis文件加載

Configutation:

  • mappedRegister 註冊接口的動態代理
  • loadedResources 填充xml文件資源
  • resultMaps 填充resultMap
  • sqlFragments 填充sql元素
  • mappedStatements 填充mappedStatment
  • keyGenerators 填充KeyGenerator

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse

  public void parse() {
	//判斷是否已經加載該配置文件
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));//處理mapper節點
      configuration.addLoadedResource(resource);//將mapper文件添加到configuration.loadedResources中
      bindMapperForNamespace();//註冊mapper接口
    }
    //處理解析失敗的ResultMap節點
    parsePendingResultMaps();
    //處理解析失敗的CacheRef節點
    parsePendingCacheRefs();
    //處理解析失敗的Sql語句節點
    parsePendingStatements();
  }

binging模塊
他是做什麼的?
她就是動態代理,執行sql。

什麼是mybatis的動態代理呢?

  • 找到session中對應的方法執行
  • 找到命名空間和方法名
  • 傳遞參數

MapperRegister:mapper接口和對應的代理對象工廠的註冊中心;
MapperProxyFactory: 用於生成mapper接口動態代理的實例對象;
MapperProxy: 實現了InvocationHandler接口,他是增強mapper接口的實現;
MapperMethod: 封裝了Mapper接口中對應的方法的信息,以及對應的sql語句的信息;他是mapper接口與映射文件中sql語句的橋樑

mybatis的接口層

org.apache.ibatis.binding.MapperRegistry

 //記錄了mapper接口與對應MapperProxyFactory之間的關係
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

org.apache.ibatis.binding.MapperProxyFactory

/**
 * 
 * 用於生成mapper接口動態代理的實例對象;
 * @author Lasse Voss
 */
public class MapperProxyFactory<T> {

  //mapper接口的class對象
  private final Class<T> mapperInterface;
//key是mapper接口中的某個方法的method對象,value是對應的MapperMethod,MapperMethod對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
	//創建實現了mapper接口的動態代理對象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
	 //每次調用都會創建新的MapperProxy對象
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

org.apache.ibatis.binding.MapperProxy

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;//記錄關聯的sqlsession對象
  private final Class<T> mapperInterface;//mapper接口對應的class對象;
//key是mapper接口中的某個方法的method對象,value是對應的MapperMethod,MapperMethod對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增強
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //從緩存中獲取mapperMethod對象,如果緩存中沒有,則創建一個,並添加到緩存中
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //調用execute方法執行sql
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  }

  @UsesJava7
  private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
      throws Throwable {
    final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
        .getDeclaredConstructor(Class.class, int.class);
    if (!constructor.isAccessible()) {
      constructor.setAccessible(true);
    }
    final Class<?> declaringClass = method.getDeclaringClass();
    return constructor
        .newInstance(declaringClass,
            MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
        .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
  }

  /**
   * Backport of java.lang.reflect.Method#isDefault()
   */
  private boolean isDefaultMethod(Method method) {
    return (method.getModifiers()
        & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
        && method.getDeclaringClass().isInterface();
  }
}

封裝方法的數據結構

public class MapperMethod {
  //從configuration中獲取方法的命名空間.方法名以及SQL語句的類型
  private final SqlCommand command;
  //封裝mapper接口方法的相關信息(入參,返回類型);
  private final MethodSignature method;
..............................
}

org.apache.ibatis.binding.MapperMethod.MethodSignature

public static class MethodSignature {

    private final boolean returnsMany;//返回參數是否爲集合類型或數組
    private final boolean returnsMap;//返回參數是否爲map
    private final boolean returnsVoid;//返回值爲空
    private final boolean returnsCursor;//返回值是否爲遊標類型
    private final boolean returnsOptional;//返回值是否爲Optional
    private final Class<?> returnType;//返回值類型
    private final String mapKey;
    private final Integer resultHandlerIndex;
    private final Integer rowBoundsIndex;
    private final ParamNameResolver paramNameResolver;//該方法的參數解析器
    ......................................
    }

org.apache.ibatis.binding.MapperProxy#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {//如果是Object本身的方法不增強
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //從緩存中獲取mapperMethod對象,如果緩存中沒有,則創建一個,並添加到緩存中
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //調用execute方法執行sql
    return mapperMethod.execute(sqlSession, args);
  }

org.apache.ibatis.binding.MapperMethod#execute

 public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //根據sql語句類型以及接口返回的參數選擇調用不同的
    switch (command.getType()) {
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {//返回值爲void
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {//返回值爲集合或者數組
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {//返回值爲map
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {//返回值爲遊標
          result = executeForCursor(sqlSession, args);
        } else {//處理返回爲單一對象的情況
          //通過參數解析器解析解析參數
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional() &&
              (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = OptionalUtil.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章