欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

mybatis源码学习------binding模块(上)

程序员文章站 2022-07-12 22:37:59
...

简介

Mybatis的Binding模块用于实现Mapper接口与Mapper.xml绑定,当用于定义了AuthorMapper.java和AuthorMapper.xml后,mybatis需要将AuthorMapper.java中定义的方法与AuthorMapper.xml中定义的sql语句关联起来,并封装到指定的数据结构中便于后续上层应用的调用。如果在绑定的过程中发现存在无法关联的sql语句,则mybatis会在初始化时抛出异常,而不是等到用户实际调用的时候再抛出异常。

问题: 平时的业务代码调用的都是xxxMapper.java中定义的接口方法,且并没有为该方法提供实现,mybatis是如何通过xxxMapper接口找到对应的sql语句的?由是如何将业务代码中传入的实参传给sql语句的?

本篇文章将会分析mybatis的org.apache.ibatis.binding包中的代码,并试图回答上面的问题。

binding模块中的类如下图所示;
mybatis源码学习------binding模块(上)
类图如下所示:
mybatis源码学习------binding模块(上)

接下来按照类图从上到下对逐个类进行分析学习

MapperRegistry

属性

MapperRegistry是Mapper接口和MapperProxyFactory的注册表,MapperRegistry类定义了两个属性:

  • Configuration config:保存着mybatis全局的配置信息
  • Map<Class, MapperProxyFactory> knownMappers :维护Mapper接口与MapperProxyFactory之间的对应关系

既然是注册表就一定少不了增删改查的方法,该类中的核心方法为addMapper(class type),接下来重点分析,其余方法直接看注释就可以。

public class MapperRegistry {
  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
    
  public MapperRegistry(Configuration config) {
    this.config = config;
  }

  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //从knownMappers中查找指定type对应的MapperProxyFactory实例
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //调用MapperProxyFactory的方法创建Mapper的代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

  public <T> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
  }

  /**
   * Gets the mappers.
   *
   * @return the mappers
   * @since 3.2.2
   */
  public Collection<Class<?>> getMappers() {
    return Collections.unmodifiableCollection(knownMappers.keySet());
  }

  /**
   * Adds the mappers.
   *
   * @param packageName
   *          the package name
   * @param superType
   *          the super type
   * @since 3.2.2
   *
   * 批量添加Mapper
   */
  public void addMappers(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }

  /**
   * Adds the mappers.
   *
   * @param packageName
   *          the package name
   * @since 3.2.2
   *
   * 将packageName包下的所有Mapper添加到knownMappers中
   */
  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }

}

addMapper(Class)

public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {//mapper必须是接口
    if (hasMapper(type)) {//防止重复添加
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      //标记1
      knownMappers.put(type, new MapperProxyFactory<>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

MapperProxyFactory

MapperProxyFactory类主要用于创建实现了mapperInterface接口的MapperProxy的对象。

public class MapperProxyFactory<T> {
  //被代理的类
  private final Class<T> mapperInterface;
  //key为mapperInterface类中定义的方法Method对象,value为与之对应的MapperMethod对象
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

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

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

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

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    //创建代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  //调用重载方法,创建mapperInterface的代理类
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
}

newInstance方法最终创建了一个实现了mapperInterface接口的代理对象,传入的是MapperProxy实例,接下来分析MapperProxy类的实现。

MapperProxy

从下面MapperProxy类的定义可以看到,MapperProxy是一个实现了InvocationHandler接口的类,这是JDK动态代理的规范,所以MapperProxy类实现的Invoke方法将会是整个类的核心逻辑。

public class MapperProxy<T> implements InvocationHandler, Serializable {......}

属性

private static final long serialVersionUID = -4724728412955527868L;
//定义了允许访问何种访问权限的方法
private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
    | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;

private static final Constructor<Lookup> lookupConstructor;//方法句柄对象的构造函数
private static final Method privateLookupInMethod;

private final SqlSession sqlSession;//关联的SqlSession对象
private final Class<T> mapperInterface;//被代理的接口
private final Map<Method, MapperMethodInvoker> methodCache;//用于维护mapperInterface中定义的方法和MapperMethod对象的映射关系

静态代码块

静态代码块中的主要逻辑是找到MethodHandle的构造函数对象,便于后续创建MethodHandle对象,但是考虑到JDK8与JDK9版本对MethodHandle实现的差异,其余代码都是为了能够兼容版本差异。

关于MethodHandle,简单来说就是JDK7发布的一套和反射有关的API,可以看做是反射2.0,感兴趣的话可以查看这篇文章进行学习https://blog.csdn.net/qq_31865983/article/details/102759032?utm_medium=distribute.pc_relevant.none-task-blog-title-2&spm=1001.2101.3001.4242

static {
  //JDK 9 的方法
  Method privateLookupIn;
  try {
    privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
  } catch (NoSuchMethodException e) {
    privateLookupIn = null;
  }
  privateLookupInMethod = privateLookupIn;
  //JDK 8 的方法获取MethodHandles的构造函数
  Constructor<Lookup> lookup = null;
  if (privateLookupInMethod == null) {
    // JDK 1.8
    try {
      //获取指定参数的构造器
      lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
      lookup.setAccessible(true);
    } catch (NoSuchMethodException e) {
      throw new IllegalStateException(
          "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
          e);
    } catch (Exception e) {
      lookup = null;
    }
  }
  lookupConstructor = lookup;
}

invoke方法

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    //如果当前调用的是Object类中定义的方法则不对其进行代理
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else {//如果调用的是Mapper接口中定义的方法则调用其代理方法
      return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

cachedInvoker

cachedInvoker方法主要用于维护methodCache集合,涉及的MapperMethodInvoker接口及其实现类会在后面分析,代码如下:

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
  try {
    // A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
    // It should be removed once the fix is backported to Java 8 or
    // MyBatis drops Java 8 support. See gh-1929
    //如果methodCache已经缓存了对应的MapperMethod则直接返回
    MapperMethodInvoker invoker = methodCache.get(method);
    if (invoker != null) {
      return invoker;
    }
    //否则将其添加到methodCache集合中
    return methodCache.computeIfAbsent(method, m -> {
      if (m.isDefault()) {//如果是default关键字定义的方法
        try {
          if (privateLookupInMethod == null) {
            //获取method对应的方法句柄,并创建DefaultMethodInvoker对象
            return new DefaultMethodInvoker(getMethodHandleJava8(method));
          } else {
            return new DefaultMethodInvoker(getMethodHandleJava9(method));
          }
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException
            | NoSuchMethodException e) {
          throw new RuntimeException(e);
        }
      } else {//是Mapper接口中定义的接口,通过创建的MapperMethod实例构造PlainMethodInvoker对象
        return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      }
    });
  } catch (RuntimeException re) {
    Throwable cause = re.getCause();
    throw cause == null ? re : cause;
  }
}

内部类

MapperMethod调用器接口及其实现类存在的意义在于屏蔽default方法和Mapper接口中定义的方法在调用方式上的差异,对上层暴露一个统一的API。

MapperMethod调用器接口定义如下:

interface MapperMethodInvoker {
  Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
}

default修饰的默认方法的调用器实现

private static class DefaultMethodInvoker implements MapperMethodInvoker {
  private final MethodHandle methodHandle;

  public DefaultMethodInvoker(MethodHandle methodHandle) {
    super();
    this.methodHandle = methodHandle;
  }

  //通过方法句柄调用
  @Override
  public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
    return methodHandle.bindTo(proxy).invokeWithArguments(args);
  }
}

Mapper接口中定义的方法的调用器实现

private static class PlainMethodInvoker implements MapperMethodInvoker {
  private final MapperMethod mapperMethod;

  public PlainMethodInvoker(MapperMethod mapperMethod) {
    super();
    this.mapperMethod = mapperMethod;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
    return mapperMethod.execute(sqlSession, args);
  }
}

MapperMethod调用器接口及其实现类的类图如下:
mybatis源码学习------binding模块(上)

getMethodHandleJava8

将method对象转换成一个方法句柄

private MethodHandle getMethodHandleJava8(Method method)
    throws IllegalAccessException, InstantiationException, InvocationTargetException {
  final Class<?> declaringClass = method.getDeclaringClass();
  //unreflectSpecial 用于将method对象转换成一个方法句柄
  return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
}

MapperMethod

MapperMethod类中保存着Mapper接口中定义的所有方法的信息,以及该方法对应的SQL语句的信息,MapperMethod是Method到sql映射的桥梁。

属性

private final SqlCommand command;//记录了sql语句的namespace和类型
private final MethodSignature method;//记录Mapper接口中定义的方法相关的信息

SqlCommand和MethodSignature都是MapperMethod类中定义的内部类,接下来分析这两个内部类的代码

内部类

ParamMap

只重写了HashMap的get方法,当key不存在时,抛出一个异常

public static class ParamMap<V> extends HashMap<String, V> {

  private static final long serialVersionUID = -2212268410512043556L;

  @Override
  public V get(Object key) {
    if (!super.containsKey(key)) {
      throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
    }
    return super.get(key);
  }

}

SqlCommand

public static class SqlCommand {
  //sql语句的名称,namespace + sqlId
  private final String name;
  //sql语句的类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
  private final SqlCommandType type;

  //构造函数,用于初始化name和type属性的值
  public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
    final String methodName = method.getName();
    final Class<?> declaringClass = method.getDeclaringClass();
    MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
        configuration);
    if (ms == null) {
      if (method.getAnnotation(Flush.class) != null) {//处理@Flush注解
        name = null;
        type = SqlCommandType.FLUSH;
      } else {
        throw new BindingException("Invalid bound statement (not found): "
            + mapperInterface.getName() + "." + methodName);
      }
    } else {
      name = ms.getId();
      type = ms.getSqlCommandType();
      if (type == SqlCommandType.UNKNOWN) {
        throw new BindingException("Unknown execution method for: " + name);
      }
    }
  }

  public String getName() {
    return name;
  }

  public SqlCommandType getType() {
    return type;
  }

  /**
   * 递归向上查找所有父类,定位定义方法的接口
   * @param mapperInterface 被代理的接口
   * @param methodName 方法名
   * @param declaringClass 生命方法的类
   * @param configuration
   * @return
   */
  private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
      Class<?> declaringClass, Configuration configuration) {
    String statementId = mapperInterface.getName() + "." + methodName;
    if (configuration.hasStatement(statementId)) {
      return configuration.getMappedStatement(statementId);
    } else if (mapperInterface.equals(declaringClass)) {
      //递归退出条件,表示在mapperInterface的整个继承树中没有找到
      return null;
    }
    //递归向上查找整个继承树
    for (Class<?> superInterface : mapperInterface.getInterfaces()) {
      if (declaringClass.isAssignableFrom(superInterface)) {
        MappedStatement ms = resolveMappedStatement(superInterface, methodName,
            declaringClass, configuration);
        if (ms != null) {
          return ms;
        }
      }
    }
    return null;
  }
}

MethodSignature

属性

//是否返回多个结果,即返回的类型是否是Collection或者数组
private final boolean returnsMany;
//返回的结果是否是Map类型
private final boolean returnsMap;
//是否为空返回
private final boolean returnsVoid;
//是否返回游标
private final boolean returnsCursor;
//是否返回一个Optional对象,jdk8中引入
private final boolean returnsOptional;
//返回的类型
private final Class<?> returnType;
//key的名称,前提是返回的类型必须是Map类型,即returnsMap应该为true
//不理解这个属性的意思的话,可以google一下@MapKey注解的功能就懂了
private final String mapKey;
//ResultHandler在方法形参中的位置
private final Integer resultHandlerIndex;
//RowBounds在方法形参中的位置
private final Integer rowBoundsIndex;
//参数名称解析器
private final ParamNameResolver paramNameResolver;

构造函数

//对上面所有属性进行初始化
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
  //调用org.apache.ibatis.reflection.TypeParameterResolver解析方法的返回值类型
  Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
  if (resolvedReturnType instanceof Class<?>) {
    this.returnType = (Class<?>) resolvedReturnType;
  } else if (resolvedReturnType instanceof ParameterizedType) {
    this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
  } else {
    this.returnType = method.getReturnType();
  }
  this.returnsVoid = void.class.equals(this.returnType);
  //调用Reflector包下的ObjectFactory判断方法的返回值类型是否是集合或数组
  this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
  this.returnsCursor = Cursor.class.equals(this.returnType);
  this.returnsOptional = Optional.class.equals(this.returnType);
  this.mapKey = getMapKey(method);
  this.returnsMap = this.mapKey != null;
  //获取RowBounds在方法形参中的位置索引
  this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
  //获取ResultHandler在方法形参中的位置索引
  this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
  this.paramNameResolver = new ParamNameResolver(configuration, method);
}

getUniqueParamIndex

用于定位paramType在方法形参中的位置索引,索引从0开始

private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
  Integer index = null;
  final Class<?>[] argTypes = method.getParameterTypes();
  for (int i = 0; i < argTypes.length; i++) {
    if (paramType.isAssignableFrom(argTypes[i])) {
      if (index == null) {
        index = i;
      } else {
        throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
      }
    }
  }
  return index;
}

ParamNameResolver

参数名称解析器,用于处理Mapper接口中定义的方法的参数列表

public class ParamNameResolver {

  public static final String GENERIC_NAME_PREFIX = "param";

  private final boolean useActualParamName;

  private final SortedMap<Integer, String> names;

  private boolean hasParamAnnotation;

  public ParamNameResolver(Configuration config, Method method) {
    //是否允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项 默认为true
    this.useActualParamName = config.isUseActualParamName();
    //获取方法的参数类型列表
    final Class<?>[] paramTypes = method.getParameterTypes();
    //获取参数上的注解,因为一个参数可以定义多个注解,所以这里是一个二维数组
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    //维护参数在方法形参中的位置索引与参数名称的映射关系
    final SortedMap<Integer, String> map = new TreeMap<>();
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    //遍历的长度为paramCount即带注解的参数个数,所以下面遍历注解集合的时候不存在索引越界异常
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      //排除RowBounds和ResultHandler这类非业务的功能性参数
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;//只要有一个参数被@Param注解修饰,该属性就为true
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        //没有被@Param注解修饰,但是允许使用方法签名中的名称作为语句参数名称,则使用方法签名中的名称
        if (useActualParamName) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          //如果当前形参既没有被@Param注解修饰,useActualParamName属性也为false
          //则当前参数的name为数组的size
          name = String.valueOf(map.size());
        }
      }
      //如果方法的形参中没有定义RowBounds和ResultHandler,且所有的形参都没有被@Param注解修饰
      //在这种理想情况下map中的值为{0="0",1="1",2="2",3="3"}
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }

  private String getActualParamName(Method method, int paramIndex) {
    return ParamNameUtil.getParamNames(method).get(paramIndex);
  }

  private static boolean isSpecialParameter(Class<?> clazz) {
    return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
  }

  public String[] getNames() {
    return names.values().toArray(new String[0]);
  }

  public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {//无参的情况
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {//只有一个参数的情况,可以做特殊处理
      Object value = args[names.firstKey()];
      return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);//name -> 实参
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {//如果用户在@Param注解中声明的变量名称就是Params+索引的格式,则跳过
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

  public static Object wrapToMapIfCollection(Object object, String actualParamName) {
    if (object instanceof Collection) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    } else if (object != null && object.getClass().isArray()) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("array", object);
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    }
    return object;
  }

}